- Notifications
You must be signed in to change notification settings - Fork27k
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Authors:@alxhub,@pkozlowski-opensource,@AndrewKushnir,@atscott A few months ago, we published an RFC for the design of standalone components, directives, and pipes. This is a project with an ambitious goal: to streamline the authoring of Angular applications by reducing the need for NgModules. The design was positively received by the community with over 140 discussion comments on the initial RFC. This RFC complements that first proposal, and explores how standalone components will be integrated into Angular's API surface to achieve the goal of authoring applications without writing NgModules. It focuses on four areas where NgModules feature prominently today:
GoalsIn designing these APIs, we focused on a handful of high-level goals:
ProvidersA central role played by NgModules today is the configuration of dependency injection, and therefore of application and library functionality. Applications import NgModules, sometimes via helper functions such as We believe that DI configuration can be cleanly achieved without NgModules, by working with providers and provider arrays directly. For example, today the Angular router is configured via Instead, the router could expose separate functions for configuring router behavior, and declaring routes. This could look like: import{configureRouter,withRoutes}from'@angular/router';providers:[configureRouter({/* router configuration */}),withRoutes([…/* route declarations */]),], As we work to reduce the complexity of NgModules in Angular, we plan to guide the ecosystem towards a providers-first approach, and away from using NgModules as configuration containers. New APIs: BootstrappingThe traditional flow of bootstrapping an Angular application is to declare an // First, the developer defines an AppModule which represents the application// to be bootstrapped.@NgModule({// This NgModule configures the application injector, via explicit providers// and imported dependencies.providers:[AppService,…],imports:[// For example, it might configure the router for the application.RouterModule.forRoot([…]),// AppModule is also conventionally responsible for configuring Angular for// the current rendering environment, by importing the correct platform// NgModule. In this case, Angular is configured for browser rendering.BrowserModule,],// AppModule specifies which component(s) should be bootstrapped onto// existing elements in the DOM.bootstrap:[RootComponent],// Conventionally, AppModule also manages the RootComponent's template by declaring it.// This also means that RootComponent's template dependencies are mixed into the// imports of the AppModule, along with application configuration.declarations:[RootComponent],})exportclassAppModule{}// Next, the developer must obtain a reference to the "platform" on the page// (a PlatformRef instance). The call also allows adding extra providers to// the "platform" level injector.constplatform=platformBrowser();// The PlatformRef is then used to bootstrap the application. Typically this step// is combined with the previous, without the intermediate `platform` variable,// but they are shown here separately to illustrate the different steps involved.platform.bootstrapModule(AppModule);// From this point, Angular waits for any application initialization Promises// (provided via the APP_INITIALIZER DI token) to resolve, and then bootstraps// the component within the NgZone associated with the application. To support bootstrapping without an NgModule, a new function // Platform creation is implicit, and the new API is focused around// the instantiation of an "application". The root component for this// application is named here directly, and must be standalone.bootstrapApplication(AppComponent,{// DI for the application is configured by specifying providers// directly.providers:[{provide:AuthService,useClass:JwtAuthService},…,// To configure libraries which are using NgModules, the// `importProvidersFrom` function provides a bridge back to the// NgModule world.importProvidersFrom(RouterModule.forRoot([…],{…})),],}); The Fallback to NgModule-based bootstrapFor more advanced use cases than
New APIs: Router and lazy loadingThe Angular Router supports lazy loading of a "part" of the application, expressed as a separate NgModule. This application part serves two distinct purposes:
We believe these are two distinct use cases, and will decouple them in the router's new standalone APIs. This leads to three new capabilities for a route definition:
Lazy loading a single componentIf a component is standalone, it can be lazily loaded directly for a route, without the need to declare any extra routing configuration or NgModule to load: exportconstROUTES:Route[]=[{path:'lazy',loadComponent:()=>import('./lazy-cmp').then(m=>m.LazyCmp)},]; To use this API, the component being loaded must be standalone. Lazily loading additional child routesToday, // app.ts:{path:'admin',loadChildren:()=>import('./admin').then(m=>m.ROUTES)}// admin.ts:exportconstROUTES:Route[]=[{path:'users',component:AdminUsersCmp},{path:'teams',component:AdminTeamsCmp},]; Separate injectors for a routeIn some cases, it's desirable to scope a set of services to a route or collection of routes that represent a specific part of the application. For example, an A new {path:'admin',providers:[AdminService],children:[{path:'users',component:AdminUsersCmp},{path:'teams',component:AdminTeamsCmp},],} This would create a new injector for the Lazily loading the injectorIf the // app.ts:{path:'admin',loadChildren:()=>import('./admin').then(adminModule=>adminModule.ROUTES)}// admin.ts:exportconstROUTES:Route[]=[{path:'',pathMatch:'prefix',providers:[AdminService],children:[{path:'users',component:AdminUsersCmp},{path:'teams',component:AdminTeamsCmp},],]; The lazily loaded route configuration contains a single parent route which configures the injector and declares a number of child routes. Interop with NgModulesAs with exportconstROUTES:Route[]=[{path:'',pathMatch:'full',providers:[importProvidersFrom(StoreModule.forFeature(…)),],children:[…],}]; Dynamic component instantiationA central use case discussed in the initial standalone design is that of dynamically rendering a component. In Angular today, it's easy to write code that does so while ignoring the component's NgModule, risking problems at runtime if the component or any of its dependencies require special configuration via providers. Setting the @Component({template:`<button (click)="addDynamicCmp()"> Add a dynamic component </button>`,})exportclassAppComponent{constructor(privatevcr:ViewContainerRef){}addDynamicCmp():void{this.vcr.createComponent(DynamicCmp);}} The first time that the button is clicked, a new standalone injector is created (assuming one is needed), which hosts any providers from NgModule-based dependencies of The second time the button is clicked, the previous standalone injector can be reused. This serves several purposes:
We expect that the need for these standalone injectors will be reduced over time, as libraries switch to non-NgModule based configuration and as we move further down the path of simplifying NgModules and reducing their responsibilities. Initialization logicAn interesting behavior of NgModules today is that they're eagerly instantiated in application injectors. That is, when bootstrapping an application: @NgModule({imports:[FeatureModule], …})exportclassAppModule{}platformBrowser().bootstrapModule(AppModule); Both The use of constructors for complex logic is not ideal (and is often considered ananti-pattern), and this pattern only works with NgModule-based configuration. To support initialization logic without NgModules, an {provide:INJECTOR_INITIALIZER,multi:true,useValue:()=>inject(LoadingService).markLoaded()} The Default exportsThe {path:'admin',loadChildren:()=>import('./admin.routes').then(mod=>mod.ADMIN_ROUTES),} Alternatively, we're considering extending these router APIs to recognize {path:'admin',loadChildren:()=>import('./admin.routes');} assuming that the exportdefaultconst[// admin routes]; This could reduce boilerplate when expressing lazily loaded routes, but might create confusion since the dereferencing of Specific QuestionsIn addition to general feedback on the proposed new APIs, we're interested in specific feedback on the following:
|
BetaWas this translation helpful?Give feedback.
All reactions
👍 130😄 13🎉 38❤️ 64🚀 46👀 19
Replies: 36 comments 155 replies
-
2 - IMHO NgModules are most representative of "applications" today so In order of preference:
3a - I don't see an option to pass a parentinjector to any of these APIs? Plenty of use cases where I'd want to have some kind of "headless" root injector with services I'd want to share across various components. Presumably the 3b - I also don't see an ability to pass a specific DOM node as the render target, which is required for dynamic use cases (vs the "global" query-the-document for one root element style) 4 - nope, and I think it muddies the waters. I realize the teams definition of "standalone component" probably refers to "standalone from an NgModule" but imho the actual mental model is "standalone from an application" IOW, unless you're planning to completely deprecate NgModules, it's better to stick with the concept of "NgModules-as-applications" and stick to the term "Component" (and don't use AppComponent in the examples 😉) for standalone stuff. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 7
-
I never said they were complicated, but they certainly are cumbersome. It is a burden to have to deal with NgModules and they slow down the process of writing code. They also make it difficult to refactor code as the existing tools aren't smart enough to tell you which imported NgModules are no longer being. This makes the process of removing dependencies and trimming bundle size slower and more troublesome than comtemporaries the simply use es modules. I have built multiple Angular apps and libraries because I like features like dependency injection, rxjs and the flexiblity that the directive / component APIs provide. My day job however is React and it is refreshing and productive to not have to deal with all these framework specific constructs. People will 100% opt for tools that speed them up and don't get in their way. Both Vue and Svelte are quite similar in style to Angular. The performance and ergonomics of these tools is what gets people excited to use them. I just want their to be the same level of excitement for Angular. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
I think there is still a place for NgModules, I remember that one of the problems about NgModules, is that it has too many responsibilities so possibly reducing it to one special case for advanced users might be great, Genuine Question: What features is angular behind with due to NgModules specifically, what feature does NgModules prevent angular from having that the others do? Anyway I feel I will only really know how good/bad initiative is when we actually experiment with it. I guess another question I want to understand better is, is there any functionality that will be completely lost if ngModules removed? |
BetaWas this translation helpful?Give feedback.
All reactions
-
Oh wow, I love this conversation!
Specifically, I think one of the biggest complexities with the design of NgModules is that they have multiple responsibilitieswith different scopes. NgModules are how we configure component templates, and in that sense they are extremely hierarchical with imports/exports defining "public" and "private" effects within a given NgModule. However, they also configure dependency injection, which is a global operation - providers in any NgModule in the application are visible toall components in the application - the same concepts of "public" and "private" don't apply (caveat: at least before lazy loaded injectors are considered). This dual nature makes them extremely interesting as a solution, because they enable components to "just work". If you import the NgModule for a component into your application, you can use that component in your template, and any services required by the componentor its dependencies are transitively imported into your application. You don't have to think about it! Except, actually, you do:
This dual nature of NgModules leaks through the APIs in a few different ways. When you set up lazy loading in your application, you quickly discover that you can't just lazily load a component. You need to load its NgModule - components can'tstand alone in Angular (yet!). If you're using the router for lazy loading a bunch of components at once, this is only mildly inconvenient. If you're trying to lazy load specific components in routes individually, it gets tedious. And if you're lazy loading without the router, you have even more responsibility: it'syour job to ensure the NgModule side of a component is accounted for when instantiating it. You need to load the NgModule, create an injector with the right parent, andthen you can create the component on top of this injector. So to answer@tayambamwanza's excellent question, lazy loading a component individually is one of those things we can't do today, because Angular's current components aren't guaranteed to work without their NgModule in the picture. There's no way we could offer the NgModules aren't a terrible API: they work, and they provide real value in large applications. But that doesn't mean we can't do better :) |
BetaWas this translation helpful?Give feedback.
All reactions
👍 3
-
I agree they aren't terrible. I've always been okay with NgModules as a tool for configuring the injector because its very reminiscent of how other DI solutions setup their providers (spring / dagger / guice). My first experience with Angular however was actually AngularDart which has no concept of NgModules and uses a In removing the requirement for using NgModules for compiler scope, I think it's natural to ask if we need NgModules at all. Is all that conceptual overhead worth it to just configure the injector? I have a hard time thinking of a case where an NgModule is better than a simple array of providers. Lazy loading of providers is all ready supported out of the box if we simply provide on a routed component. If you peel back the layers I think the value NgModules provides begins to erode. I for one am bullish about a future where they are no longer needed. |
BetaWas this translation helpful?Give feedback.
All reactions
-
@alxhub Thanks for explaining, never thought of that actually. |
BetaWas this translation helpful?Give feedback.
All reactions
-
This was a great RFC 🚀 Questions:
Answers:
|
BetaWas this translation helpful?Give feedback.
All reactions
-
Yes - at least as far as the base
No, they're not going away. We're trying to keep the new bootstrapping API as minimal as possible for now, and consider cases like |
BetaWas this translation helpful?Give feedback.
All reactions
👍 3
-
Component-level initializerHow about an initializer tied to the injector scope of a standalone component assuggested by Younes (@yjaaidi)? @Component({standalone:true,providers:[{provide:INJECTOR_INITIALIZER,multi:true,useValue:()=>inject(LoadingService).markLoaded()}],template:'',})exportclassMyStandaloneComponent{} |
BetaWas this translation helpful?Give feedback.
All reactions
❤️ 1
-
This is an interesting idea and something we've discussed before. Currently the thinking is not to do this, for a few reasons:
But I do see the potential for better modularity - this is something we should think about. |
BetaWas this translation helpful?Give feedback.
All reactions
👀 1
-
Thx Lars for reraising this issue 😊 |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Route support for default exportsYes, please. In addition to a named export similar to the current Router API. // app.component.tsimport{Component}from'@angular/core';import{configureRouter,RouterModule,Routes,withRoutes}from'@angular/router';constroutes:Routes=[{path:'admin',loadChildren:()=>import('./admin.routes'),// 👈},];@Component({standalone:true,imports:[RouterModule,],providers:[configureRouter({anchorScrolling:'enabled',initialNavigation:'enabledNonBlocking',scrollPositionRestoration:'enabled',}),withRoutes(routes),],template:'<router-outlet></router-outlet>'})exportclassAppComponent{} // admin.routes.ts// 👇exportdefaultconstroutes:Routes=[// or:// export default const [{path:'users',component:AdminUsersComponent},{path:'teams',component:AdminTeamsComponent},]; versus // app.component.tsimport{Component}from'@angular/core';import{configureRouter,RouterModule,Routes,withRoutes}from'@angular/router';constroutes:Routes=[{path:'admin',loadChildren:()=>import('./admin.routes').then(esModule=>esModule.routes),// 👈},];@Component({standalone:true,imports:[RouterModule,],providers:[configureRouter({anchorScrolling:'enabled',initialNavigation:'enabledNonBlocking',scrollPositionRestoration:'enabled',}),withRoutes(routes),],template:'<router-outlet></router-outlet>'})exportclassAppComponent{} // admin.routes.tsexportconstroutes:Routes=[// 👈{path:'users',component:AdminUsersComponent},{path:'teams',component:AdminTeamsComponent},]; |
BetaWas this translation helpful?Give feedback.
All reactions
👍 7👎 4
-
Definitely a good concept that may be used in the majority of lazy use-cases and reduces complexity for beginners. Nevertheless, the named exports are important to implement more advanced concepts. |
BetaWas this translation helpful?Give feedback.
All reactions
-
reposting for better visibility: |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Follow ups:
To illustrate the basic idea: @Component({template:'Hello {{name}}!'})exportclassHelloWorld{ @Input()name='World'}consthost=document.getElementById('root');constcompRef=bootstrapComponent(HelloWorld,{host});//a few moments latercompRef.instance.name='Angular';//compRef.detectChanges() ?//detectChanges(compRef) ? |
BetaWas this translation helpful?Give feedback.
All reactions
👍 11
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Very interesting questions here and parts we didn't call out in the RFC explicitly (partly to avoid "details overload" and partly because we are still debating things). But here we go:
Yes. If we import
This is not part of the "standalone" effort.
constappRef=awaitbootstrapApplication(HelloWorld);//a few moments laterappRef.components[0].instance.name='Angular';appRef.tick(); But yeh, this RFC centers the idea of "bootstrap" around the "application" concept vs. "root component" concept. |
BetaWas this translation helpful?Give feedback.
All reactions
-
How does the new standalone apis play with ngExpressEngine ? |
BetaWas this translation helpful?Give feedback.
All reactions
-
This was answered in another question, see:#45554 (reply in thread) |
BetaWas this translation helpful?Give feedback.
All reactions
-
Lazy loading components requires a "loader module" next to the component. |
BetaWas this translation helpful?Give feedback.
All reactions
👀 1
-
This proposal suggests that no Angular modules are needed to lazy load a routed standalone component: exportconstroutes:Routes=[{path:'lazy',loadComponent:()=>import('./lazy-standalone.component').then(esModule=>esModule.LazyStandaloneComponent)},]; The same goes for dynamically loaded standalone components: @Component({template:`<button (click)="addDynamicComponent()"> Add a dynamic component </button>`,})exportclassAppComponent{constructor(privatevcr:ViewContainerRef){}addDynamicComponent():void{this.vcr.createComponent(DynamicStandaloneComponent);}} I think you haverender modules in mind. Hopefully, they are not needed because a standalone component has something like a localtransitive compilation scope/local component scope. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 4
-
@LayZeeDK is correct - no NgModules are necessary to lazily load standalone components. That means no loading NgModules, and no render NgModules. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 4
-
I'm curious too., have a good work.@BioPhoton |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
TestBed APIPlease consider feedback on the extended CommonModule default import
Given this component // my-standalone.component.ts@Component({standalone:true,template:'{{ text$ | async }}',})exportclassMyStandaloneComponent{} This component would fail at compile time (AOT) or runtime (JIT) because Your suggestion in the Standalone Components RFCconstfixture=TestBed.createStandaloneComponent(MyStandaloneComponent); or with metadata overrides that must support constfixture=TestBed.createStandaloneComponent(MyStandaloneComponent,{set:{imports:[ChildComponentStub,CommonModuleStub,],},}); Younes' suggestion in the Standalone Components RFCSuggestion by Younes (@yjaaidi): constfixture=TestBed.createComponent(MyStandaloneComponent); My suggestions in the Standalone Components RFCSuggestions by me TestBed.overrideComponent(MyStandaloneComponent,{set:{imports:[ChildComponentStub,CommonModuleStub,],},});constfixture=TestBed.createComponent(MyStandaloneComponent); Legacy component with stubbed provider TestBed.configureTestingModule({declarations:[UserComponent],providers:[{provide:UserService,useClass:UserServiceStub}],});constfixture=TestBed.createComponent(UserComponent); Standalone component with provider stubbed in testing module TestBed.configureTestingModule({providers:[{provide:UserService,useClass:UserServiceStub}],});constfixture=TestBed.createComponent(UserComponent); Standalone component with provider stubbed in metadata overrides TestBed.overrideComponent(MyStandaloneComponent,{add:{providers:[{provide:UserService,useClass:UserServiceStub}]},});constfixture=TestBed.createComponent(UserComponent); Legacy component with inline template and styles TestBed.configureTestingModule({declarations:[UserComponent],});constfixture=TestBed.createComponent(UserComponent); Standalone component with inline template and styles constfixture=TestBed.createComponent(UserComponent); Legacy component with external template and styles TestBed.configureTestingModule({declarations:[UserComponent],});constfixture=TestBed.createComponent(UserComponent); Standalone component with external template and styles constfixture=TestBed.createComponent(UserComponent); Alternative suggestion by me in the Standalone Components RFCTestBed.configureTestingModule({imports:[UserComponent],providers:[{provide:UserService,useClass:UserServiceStub}],});constfixture=TestBed.createComponent(UserComponent); |
BetaWas this translation helpful?Give feedback.
All reactions
👀 1
-
Good discussion on the TestBed + standalone story! We didn't cover the |
BetaWas this translation helpful?Give feedback.
All reactions
-
We heavily provide services in the With the introduction of standalone components, I believe that TestBed API need's some improvement to help with mocking. I could see an API something like this being useful: describe("AppComponent",()=>{letfixture:ComponenetFixture<AppComponent>;letcomponent:AppComponent;beforeEach(async()=>{letauthServiceStub=jasmine.createSpyObj("AuthService",["login"]);awaitTestBed.compileComponent(AppComponent,{providers:[{provide:AuthService,useValue:authServiceStub}]});fixture=TestBed.createComponent(AppComponent);component=fixture.componentInstance;});}); |
BetaWas this translation helpful?Give feedback.
All reactions
-
We can use the TestBed.configureTestingModule({declarations:[MyCmp],providers: ...,schemas:[CUSTOM_ELEMENTS_SCHEMA]})TestBed.createComponent(MyCmp); The trick here is to make sure that |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Will NgZone be supported?Will |
BetaWas this translation helpful?Give feedback.
All reactions
-
most likely,yes :) |
BetaWas this translation helpful?Give feedback.
All reactions
-
It will only support zone-based applications. Unlike the |
BetaWas this translation helpful?Give feedback.
All reactions
👍 6
-
Thank you for the clarification, Alex |
BetaWas this translation helpful?Give feedback.
All reactions
-
bootstrapComponent optionsA. Will |
BetaWas this translation helpful?Give feedback.
All reactions
-
Would it be possible to call |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Yes, this should work just fine. That's the distinction between platform and application - the page only has one platform, but there can be many applications running on it simultaneously. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
@alxhub To sum it up,
Is this correct? // CC@manfredsteyer |
BetaWas this translation helpful?Give feedback.
All reactions
-
@mikezks I guess if you reuse the injector, the providers will be shared. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Thanks@wilmarques. There is one special edge case for multi version Micro Frontends with Module Federation, where we need to reuse the platform, but bootstrap a second independent app. It seems, that this can work with the new API like today or even a bit easier. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Which platform does bootstrapComponent use?A. Which package will host |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
This is not yet decided. I think there is a lot of interest on the team in putting the API in
Yes - it will implicitly bring in |
BetaWas this translation helpful?Give feedback.
All reactions
👍 3
-
Just to update everyone on our latest thinking: For the SSR scenarios we are going to expose the |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Great RFC! Answers to Questions: 2. The naming of |
BetaWas this translation helpful?Give feedback.
All reactions
🎉 2
-
How do you feel about the existing name of PlatformRef#bootstrapModule,@alxhub? It doesn't mentionapp orapplication. Say in a future version, bootstrapComponent would support NoopNgZone, would bootstrapApplication still be your preferred name? To me, "bootstrapping" in Angular means start an "application" whether by Angular module or component. Attaching a component with a life cycle to the DOM would be a different thing in which case bootstrapComponent wouldn't be a descriptive name. The reason I prefer bootstrapComponent is the distinction from bootstrapModule, that only a component is required. "bootstrap" is a decent name but would types be able to distinguish between passing a component and passing any other class/decorated class? |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
With the general direction of making
Yeh, "bootstrap" is generic and "vague enough" that we side-step the whole detailed discussion of "application" vs. "root component". As you've mentioned it is a decent name and an option that is still on the table. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Would the bootstrap function's types be able to tell whether we pass a (standalone) component, an Angular module, or some other class by mistake? |
BetaWas this translation helpful?Give feedback.
All reactions
-
@LayZeeDK at the type level, no. This is not possible in TypeScript, since there is currently no way for decorators to alter the type they're decorating. Perhaps this will change as decorators are formally added to JavaScript itself. |
BetaWas this translation helpful?Give feedback.
All reactions
-
In that case, should we rely on the function name, that is bootstrapComponent, and/or the parameter name, that is bootstrapApplication(component)? |
BetaWas this translation helpful?Give feedback.
All reactions
-
What about Angular Elements? Currently it's needed to create a module and bootstrap the main component on But it isn't a great opportunity to also simplify that process? |
BetaWas this translation helpful?Give feedback.
All reactions
-
Yes, and we are very much interested in ways we can improve Angular Elements. A lot of these improvements, though, go beyond the specific use of NgModules, and would depend on deeper semantic changes in how elements work (for example, how elements share their "application" / zones). This is better explored as a separate effort. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 4
-
@alxhub , that makes sense. Can't wait for that! But what if we start experimenting with something similar to what was proposed for bootstrapping applications? Maybe something like this: // We could use `bootstrapElement` instead of `bootstrapApplication`bootstrapElement(AppComponent,{providers:[ ...importProvidersFrom(RouterModule.forRoot([…],{…})),],}); |
BetaWas this translation helpful?Give feedback.
All reactions
-
I don't know of any.
I Agree with@robwormald here.
However, I disagree with Rob about the DOM node, which should be taken care of by the selector in the component. I don't see a use-case where one would need to boot the same 'top' component multiple times in a document. I don't see this as an extension/replacement for Angular Elements. (although that might be worth a discussion on its own!)
Yes, proper support for booting multiple components, using the same Injector. This will bring much of the value from micro frontends without most of their usual costs.
Well, without a ngModule, the app component becomes the app, right?
Yes, please! |
BetaWas this translation helpful?Give feedback.
All reactions
-
I haven't worked much with micro frontends personally - can you elaborate on the use case here? Do microfrontends often bootstrap multiple components at a time? Is this not possible today using the |
BetaWas this translation helpful?Give feedback.
All reactions
-
The use case is having a mono-repo with multiple teams working on different "mini-apps" that can be active on the same page, at the same time. The page can be a SPA on its own or an old-fashioned MPA. The teams can work mostly in isolation from each other. Personally, I have only tried it in lab settings and kicked it around a little bit, and its a while ago. Yes it Is possible with current tools, but there are some issues IIRC. I think@manfredsteyer can provide way more details around this as I can. |
BetaWas this translation helpful?Give feedback.
All reactions
-
I will add a comment covering all possible Micro Frontend implications. One scenario that would likely break is:
// CC@manfredsteyer |
BetaWas this translation helpful?Give feedback.
All reactions
-
Thanks for CC-ing me,@SanderElias and@mikezks. You are right: In order to support all the different "flavors" of micro frontends we find in huge companies, it would be great to a) be capable of switching out ngZone and b) bootstrap several applications using the same platform (platformBrowser). However, I understand that not all of these things can be directly supported at the beginning.@alxhub already mentioned, that there will be another initiative for dealing with Zone.js. Anyway, I think we will find ways for doing micro frontends, e. g. by sticking at least with the traditional bootstrapping and at least with one AppModule. To put it short: If a) and b) do not hurt, e. g. by providing a parameter object with a ngZone property, it will help micro frontend folks a lot. Otherwise, they can also live with it (but won't be that happy, also b/c as all of this is straightforward in other frameworks). |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
I'm not sure if I would use Angular for the use case I'm going to describe. But imagine a Design System library created using Angular Elements. If it was possible to bootstrap several components within the same project it would be possible to export each of these components as Angular Elements. Although it's possible using the currently available options, I agree with@manfredsteyer that it isn't so straightforward as LitElement, for example. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Just more conventions over configurations... I'll try to go to the extreme, just pushing boundaries a little bit :-).
2a)// just boostrap a simple app without providers.bootstrap(AppComponent); 2b)// a real app with configuration defined using InjectionTokens,...awaitbootstrap(AppComponent,[// { provide: BOOTSTRAP_OPTIONS, useValue: {...}}useBootstrapOptions({ngZone:'zone.js',ngZoneEventCoalescing:true,}),// { provide: ROUTER_OPTIONS, useValue: {...}}useRouterOptions({initialNavigation:'enabled',}),// { provide: ROUTES, useValue: [] }useRoutes([]),// ...{provide:ErrorHandler,useClass:MyErrorHandler},{provide:LOCALE_ID,useValue:'cs',},]);console.log('App is running'); And thank you for such a great RFC. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
We've learned an important lesson in years of designing APIs in Angular: that just because today we can't imagine more parameters or options to a function, doesn't mean there won't ever be new things to add to an API. So now we try to future-proof these kinds of APIs using an options parameter that can be expanded as needed with new keys. |
BetaWas this translation helpful?Give feedback.
All reactions
❤️ 8
-
I see. Thank you for the answer, Alex. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Hi, this RFC is amazing! congrats! I want to know is if you are planning to support a "ComponentWithProviders" feature to configure standalone components, similar to the ModuleWithProviders for modules. @Component({///.....})classDateTimeFormComponent{staticconfigure(localeId:string) :ComponentWithProviders{return{component:DateTimeFormComponent,providers:[{provider:LOCALE_ID,useValue:localeId}]}}constructor(@Inject(LOCALE_ID)localeId){}}@Component({standalone:true,///.....imports:[DateTimeFormComponent.configure('fr')]})classMyParentComponent{} And may will be useful for routing too. @Component({///.....})classChildComponent{staticconfigure(data) :ComponentWithProviders{return{component:DateTimeFormComponent,providers:[{provider:DATA_INJECTOR,useValue:data}]}}constructor(@Inject(DATA_INJECTOR)data){}}// router{path:'child-component',loadChildren:()=>import('./child-component').then(component=>component.configure({config:"123"})),} |
BetaWas this translation helpful?Give feedback.
All reactions
👀 2
-
No, this is not our intention. Mainly we don't want to turn components into a component + NgModule combination (in the sense of being an union of all capabilities of a componentand NgModule). |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Is it possible that Standalone Components are released before these APIs?Will you wait on figuring out these APIs before releasing the Standalone Components feature itself? Standalone Components will be useful even before the release of these APIs. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Technically nothing prevents us from doing so. And this is the order in which we implement things: standalone components, directives and pipes, interoperability with NgModules, injector story etc. Then moving to the APIs (that need the pieces mentioned before). |
BetaWas this translation helpful?Give feedback.
All reactions
👍 4👀 1
-
Yeah this would be great, I think some more new ideas will arise just from using the basic concept of standalone components for now so look forward to when they release. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2
-
I'd love to get standalone components early. I have been using the single component module pattern for my shared components for a while now and it would be great to clean up that mess. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
+1 for the default exports and separate injectors for a route. |
BetaWas this translation helpful?Give feedback.
All reactions
-
BootstrappingThe new suggested bootstrapBrowserApp({declarations:[RootComponent],providers:[{provide:AuthService,useClass:JwtAuthService},importProvidersFrom(RouterModule.forRoot([…],{…}))]}): Current With the change I now assume that in this case RoutingIn the case of route configuration extracted into separate files, I believe that adding a Could developers maybe import functions used in import{AService,BService}from"...";import{transient,scoped}from"@angular/core";@Component({selector:'lazy',providers:[transient(AService),// <- Will be destroyed when component is destroyedscoped(BService)// <- Will remain in route provider tree and reused when component is created again]})classLazyComponent{constructor(privatea:AService,privateb:BService){}} This concept borrows a bit from .NET DI where you have Default exportI initially was very pro this idea, but after thinking for a while I do believe this "magic" doesn't really provide much value. The difference between {path:'admin',loadChildren:()=>import('./admin.routes').then(chunk=>chunk.ADMIN_ROUTES),}// and{path:'admin',loadChildren:()=>import('./admin.routes');} is so few extra characters of code, IMO being explicit in this case should not be a big deal, even in enterprise applications with hundreds of routes. This will probably help with debugging as well where you actually can provide the name of the exported variable in a stacktrace. |
BetaWas this translation helpful?Give feedback.
All reactions
-
@alxhub What would be the impact of multiple bootstrapped components declared in a single root Angular module? What's their application model? |
BetaWas this translation helpful?Give feedback.
All reactions
-
@LayZeeDK Ah I missed that API specification! You are right, with introduction of |
BetaWas this translation helpful?Give feedback.
All reactions
-
@alxhub If I provide a service as I could also, with standalone components, see the use case of bootstrapping a modal container in which you could through a service provide an API to dynamically show content without polluting the |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2
-
@krilllind |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2
-
@alxhub Cédric Exbrayat and I use multiple bootstrapped components in the HTML slides of our Angular training. the slides are a regular index.html file, containing slides written with bespoke.js. And some of the slides contain a live Angular demo, implemented as a bootstrapped component. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Impact of optional-NgModules on the next milestones in Angular roadmapI'd be happy to understand whether introducing the optional-NgModules cantechnically help Angular to move forward with the next milestones from the Angular roadmap, including the latest innovations in the web industry. Thank you for your hard work for constantly improving Angular! I'd like to share my impression (maybe wrong?) that Angular compiler is very sophisticated, which (I guess?) makes the adoption of the latest innovations from web industry more tricky. The support of dynamic-imports for example was added quite lately (comparing to Vue and React) when ViewEngine was rewritten to Ivy. And I'm wondering if dropping NgModules will significantly help to simplify the Angular compiler, so Angular can adopt new innovations in the web industry more eaisly. But again, maybe it's just my wrong impression. You folks are doing great job developing Angular, many thanks! TheAngular roadmap touches - among others - the following 3 topics:
Moreover, on the horizon we can see the birth of the next generation of "resumable-SSR" freameworks (Marko and Qwik). There aresome pending works to integrate Qwik with React. It's interesting when Angular will be able to adopt similar innovations. Do you think that any of above topics will be simpler to achieve for Angular compiler when NgModules are optional or totally removed? If yes, why? |
BetaWas this translation helpful?Give feedback.
All reactions
-
I am also very interested and curious what this RFC opens up in terms of build/compile time improvements and new technologies that can be leveraged. |
BetaWas this translation helpful?Give feedback.
All reactions
-
This is a great question, especially as I think the current approach looks more like a replacement for NgModules (which again was denied), so this would add more complexity as well. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Sorry for the slow answer here :) For anything SSR/runtime performance related, standalone won't make any difference. It also won't make any difference in build performance within the current compiler architecture. But, NgModule has a really annoying property: @Component({selector:'blah-cmp',template:'<foo-cmp></foo-cmp>',})exportclassBlahCmp{} What is This isn't an issue in the current compiler, which always looks at the whole rest of the program because that's how TypeScript itself works. The current compiler also does a lot of bookkeeping to track the relationships between NgModules and components, so it can efficiently recompile the right things if e.g. the NgModule changes. This keeps incremental builds fast - there's no major gains here from Where thisdoes start to get interesting is in hypothetical future compilation approaches. Some modern JS transpilers (esbuild, swc, etc) use file-by-file transpilation, skipping TypeScript's type operations, and can achieve parallel compilation and other neat tricks. This isn't possible with Angular todaybecause of NgModules and this weird backwards dependency arrow, where components don't have direct imports to their own dependencies. |
BetaWas this translation helpful?Give feedback.
All reactions
🚀 2
-
First of all a great RFC and generally standalone components are a blessed change! One thing that was brought up in the previous RFC and I can't recall it being answered is the case of NgModules as logical grouping of related components. To be more precise - if a standalone component has some repeating pattern which fits to a new sub component - how can we make this new sub component privately usable only within the original component? With modules we would just omit it from the exports array. It seems in standalone components this is not feasible unless used in a library scope. I don't think exposing each and every component as a public standalone component even in apps is a good idea (same as private class methods). Any direction on how to solve it? Or is it only me that thinks it's a problem? |
BetaWas this translation helpful?Give feedback.
All reactions
-
You can tackle this directly with ECMAScript language features. Just create a subfolder for your module-scoped implementations and create an @IgorMinar once mentioned on twitter, that those barrel-file-imports may directly be supported inside the new Component's decorator So, to sum it up, it is not exactly the same separation, but quite close and as a bonus, it uses direct JS language features. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 3
-
@mikezks is totally right here - ECMAScript language features are the way to go! |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
@mikezks I think using native barrel-file imports is only solving part of the problem here. In large codebases it's very common that child component share the same name as other child components in another module. With modern tools like VSC allowing for automatic imports, components could easily be imported by accident from the wrong path. With current However without this metadata, you would only get this information at runtime when you realize that the wrong component was imported. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
@krilllind, you can use the same strategy as with NgRx namespace imports like |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
This RFC is just incredible -- thank you! -- and will clearly have a positive impact on many aspects, from developer experience to sustainability. I see Standalone Components as a great opportunity to improve the mental model of Angular applications. However, I feel like we are trying delegate a lot to the Router and I'm not convinced that it should be responsible for all. Maybe I'm totally wrong but why couldn't we keep some of those properties/metadata in the components themselves? It would allow us to:
I know this goes totally against the current project and router but like I said, this RFC is great opportunity to discuss. Example: // my-routed-component.ts@Component({path:'something',strategy:'lazy'// 'eager'children:[MyChildrenComponent,MyChildrenComponentFoo,MyChildrenComponentBar ...]})exportclassMyRoutedComponent{}// routes.ts, if anyexportconstRoutes=[MyRoutedComponent,MyRoutedComponentFoo,MyRoutedComponentBar ...] Where routed components may have slightly more properties than they do now but it makes sense to me that these belong to the component instead of the Router. Also, routed component don't need a And where Another example // my-route-config.tsexportconstMyRouteConfig=[{path:'something',strategy:'lazy'// 'eager'children:[], ...}]// my-routed-component.ts@Component({route:MyRouteConfig, ...})exportclassMyRoutedComponent{} I have this feeling where the RouterModule could totally lookup routes directly from the components so we don't have to declare them anywhere. This is me thinking outloud but as mentioned, I think this RFC is a good opportunity to discuss. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
This might not be the optimal solution, but I actually like the idea of component containing metadata of whether it should be lazy loaded or not and what path it belongs too then just passing the component to a consumer that would read those properties. So when working on that component I could see any route metadata there and then rather than opening up the "routing file". That's just me though. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Just a quick note: we did not intend to change how the router configuration works with We can certainly discuss the router <-> component interplay and I do here your ideas / concerns but this is out of scope of this particular RFC. There are probably tons of considerations when it comes to router and components interactions so it would definitively require a specific and detailed design. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2
-
Wow, awesome guys, I feel a little less wrong. @krilllind totally, this was a very broad idea but overall, it would be really interesting to have the router looking up path instead of declaring every route and I'm sure it feasable without changing too much of the RouterModule, just the logic that receive the path. Regarding lazy load or eager, this could be something moved to the builder without necessarily have to load the component at runtime to know about the route but yeah, like I said, it's a very broad idea of an eventual RouteModule automatic route lookup, idk. @tayambamwanza thanks mate, I guess I was a little off topic but all the discussion around the routes drawn my attention and this bumped into my head 😅 @pkozlowski-opensource I totally gotcha, I've been digressing from the RFC, it's just that it's a little scary to have many properties on the route list but this is just me. |
BetaWas this translation helpful?Give feedback.
All reactions
-
I guess where I question that approach just at a glance is the impact that keeping routes with the components would have on flexibility and potential for re-use. If I built a form component that depending on the circumstances could be used in a (child) route situation, in some segment of my page, but could also be displayed in a modal on a different screen or for a different use case, that doesn't really work if you've embedded route information into the component. Decoupling routes from components allows for more re-use options and keeps the components you build agnostic of where exactly they're being used. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2
-
@michaelfaith that's true, I thought about that too but, from my experience, most routed components (modules ftw) are not made to be re-usable. Also, there's always trade-offs and things to be cleared, the base idea is to avoid redundant coding practices and have the Router discovering routes he has to serve while keeping the responsibility of the path and some other properties at component level, that's actually quite what we do with NgModules at the moment where children belong to the module and not directly the router. But I agree, this is very broad and eventually needs clearance. |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
@alxhub@pkozlowski-opensource
There is some discussion in this RFC where some peole think this is "not the best idea"™️. I do disagree, and think it might be the case for those people and should be taken care of with an optioal lint rule. // In someModue:exportdefaultconstsomeThing=classmyModule{/* code here */}// in the importing moduleconstmyModule=import('someModule').then(m=>m.someThing)// VersusconstmyModule1=import('someModule')) They will both represent the same export. That made me curious. Is there a way to differentiate? EDIT: After rereading this, I thought it might come of as snarky. Sorry for that, that isn't the case. |
BetaWas this translation helpful?Give feedback.
All reactions
❤️ 1
-
@pkozlowski-opensource Ok just did are-read of the spec. I think it will bevery hard to tell the difference. As far as I can see, the things that make it different are not exposed to the runtime. Still would be interested, it might be something that becomes useful to me! |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
@pkozlowski-opensource I am very sorry to hear, that default exports are not supported at least at the beginning. I understand the concerns mentioned in this RFC, but those new standalone APIs offer that much clean concepts back to the roots of JS/TS that is really a pity, that we do not support this plain language feature here. People that do not like that, should use linting rules to disallow this in their codebase. |
BetaWas this translation helpful?Give feedback.
All reactions
-
@mikezks just at the risk of over-communicating: you can absolutely use default exports toexport routes config. We just don't plan to do (at least not initially) and special treatment of those exports when using standard JS |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
Yes, you are right, |
BetaWas this translation helpful?Give feedback.
All reactions
😄 1
-
Nope, sadly. Dynamic import ( This is the "magic" that we're talking about adding - the router could look for a |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
I'm assuming this is what you mean by the constructor anti-pattern? Yes, it would be great if I could have a function that just runs this code so I don't have to do this sweet NgModule constructor trick. Import this module and I have NgIdle up and running. |
BetaWas this translation helpful?Give feedback.
All reactions
-
This is what |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Well, it's not available to use yet is it? Maybe, it would look something like this? Where exactly would this code go? Do I now move this to the constructor of There's also the import of |
BetaWas this translation helpful?Give feedback.
All reactions
-
What about services that are:
I think that I missed something 🤔 Here is an example to make this clear Challenge: Wrapping non-treeshakable, or module-depending servicesThe following pattern is useful in order to hide implementation details (i.e. dependencies). @Injectable()exportclassNotifier{}@NgModule({providers:[Notifier],imports:[MatDialogModule,MatToastModule],})exportclassNotifierModule{} A common similar scenario is the facade pattern with state management so that we don't forget to import the feature store before using the facade. @Injectable()exportclassMyFacade{}@NgModule({providers:[MyFacade],imports:[StoreModule.forFeature(...)],})exportclassMyFacadeModule{} These modules could be used this way @Component({standalone:true,imports:[MyFacadeModule,NotifierModule]})exportclassMyComponent{} Possible full standalone (but not equivalent) solutions with the current RFCWhat would be the full-standalonish alternative? classNotifier{staticproviders=[Notifier,importProvidersFrom(MatDialogModule), ...];}classMyFacade{staticproviders=[MyFacade,importProvidersFrom(StoreModule.forFeature(), ...]}; but then, where should we provide them? a. in root injector with |
BetaWas this translation helpful?Give feedback.
All reactions
-
I just recently posted a feature request to allow providers inside injectables... Now that you mention it in the standalone context it seems to me even more correct... |
BetaWas this translation helpful?Give feedback.
All reactions
-
Hi@yjaaidi,
In that case, you can expose a providers array: exportconstNOTIFIER_SERVICES=[importProvidersFrom(StoreModule.forFeature(...)),NotifierService,]; Users can then import I understand that this approach seems more burdensome than putting This explicit separation between DI and components though is exactly the intention of standalone: separating the configuration of DI (via providers) from the configuration of components (via imports). It's very intentional that components do not bring their own DI configurationfor the rest of the application. In this way, there's a separation of concerns between components and the rest of the app, and components aren't responsible for configuring things bigger than themselves. |
BetaWas this translation helpful?Give feedback.
All reactions
👍 2
-
Thx@alxhub for the clear response. This paragraph is a precious gem that should be part of the upcoming docs 😉
|
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Demo repoHey, I set up this demo repo which uses the Standalone APIs preview (currently
|
BetaWas this translation helpful?Give feedback.
All reactions
👍 4🚀 5👀 1
-
Nice demonstration. Thanks for putting that together. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Very nice@LayZeeDK - thnx for putting it together. If there would be anything to suggest I would consider demonstrating the fact that one can import existing Other thing that I'm seeing people tripping over is DI configuration in the But yeh, those initial demos are so important so thnx so much@LayZeeDK for doing this one! |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1
-
@LayZeeDK can you please also add a stackblitz link. Having seen this demo I'm a bit more comfortable with this change. Although maybe just call zippy-app app, because I think it would help to make it look as familiar as possible, if I see plain app.component.ts there. Then also as mentioned an example of importing a module, or at least a seperate demo if you want this one not to have any modules. |
BetaWas this translation helpful?Give feedback.
All reactions
-
@tayambamwanza not a stackblitz, but:https://layzeedk.github.io/ngx-zippy-standalone/ |
BetaWas this translation helpful?Give feedback.
All reactions
-
No, please don't add any magic. IDE will not recognize this convention, part of developers will be confused as well. |
BetaWas this translation helpful?Give feedback.
All reactions
-
I've tried to convert one of my components (which is "scam") into standalone components and found this issue: I'm using NgRx ComponentStore, code initially was: @Component({selector:'scs-editor',templateUrl:'./editor.component.html',styleUrls:['./editor.component.scss'],changeDetection:ChangeDetectionStrategy.OnPush,providers:[EditorStore]/// 👈 will initialize a new EditorStore instance})exportclassEditorComponent{publicreadonlystate$:Observable<EditorState>;publicreadonlytextCtrl:FormControl<string|null>;constructor(privatereadonlystore:EditorStore,){this.state$=this.store.state$;this.textCtrl=this.store.textCtrl;}save(){this.store.save$();}localModelDataLoaded(modelData:string){this.store.patchState({model:modelData});}}@NgModule({imports:[CommonModule,ReactiveFormsModule,MatInputModule,MatButtonModule,MatIconModule,MatProgressSpinnerModule,LocalStorageMenuComponentModule,],providers:[AppStore],/// 👈 will use existing AppStore instancedeclarations:[EditorComponent],exports:[EditorComponent],})exportclassEditorComponentModule{} I've replaced it with: @Component({selector:'scs-editor',templateUrl:'./editor.component.html',styleUrls:['./editor.component.scss'],changeDetection:ChangeDetectionStrategy.OnPush,standalone:true,providers:[EditorStore,AppStore],/// 👈 it's obviously not the same, how should I write it?imports:[CommonModule,ReactiveFormsModule,MatInputModule,MatButtonModule,MatIconModule,MatProgressSpinnerModule,LocalStorageMenuComponentModule,],})exportclassEditorComponent{publicreadonlystate$:Observable<EditorState>;publicreadonlytextCtrl:FormControl<string|null>;constructor(privatereadonlystore:EditorStore,){this.state$=this.store.state$;this.textCtrl=this.store.textCtrl;}save(){this.store.save$();}localModelDataLoaded(modelData:string){this.store.patchState({model:modelData});}} As you can see, I'm accessing the global state store using "providers" of NgModule and creating a local store using "providers" of the component. Maybe it's just some misunderstanding of the APIs - please let me know how to fix it :) Code runs fine, but the AppStore is not initialized ;) |
BetaWas this translation helpful?Give feedback.
All reactions
-
I have a single-component-module in a library. I can use NgModule.providers to make it's store globally accessible. I can not access bootstrapApplication to do this - as I understand, this function should be called once.
https://ngrx.io/guide/component-store/initialization - please take a look. Sorry, this night I'll have time to create a stackblitz. |
BetaWas this translation helpful?Give feedback.
All reactions
-
Right, as a library author you can't call As for libraries - the suggested way of doing it would be to convert @Injectable({provideIn:"root"})classAppStore(){} This is the pattern we've been suggesting for quite some time (and will probably double-down on this recommendation). |
BetaWas this translation helpful?Give feedback.
All reactions
👍 1❤️ 1
-
@pkozlowski-opensource it works great. I was afraid that because of "{providedIn: 'root'}" ngOnDestroy will not be triggered, but it works fine. Thank you! |
BetaWas this translation helpful?Give feedback.
All reactions
-
I've converted a few components, and I can tell that it removes a LOT of boilerplate :) |
BetaWas this translation helpful?Give feedback.
All reactions
-
Yay, great to hear this! |
BetaWas this translation helpful?Give feedback.
All reactions
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
tl;dr; thank you for all the feedback - our intention is to implement APIs described in this RFC and roll them out in developer preview for Angular v14! Shipping it as a developer preview in Angular v14!First of all, we would like to thank everyone who commented on, asked questions, or otherwise engaged in the discussion on this RFC. The high quality input from the community sparked lots of useful discussions and allowed us to make adjustments to the initial design. Based on all the comments and feedback we didn't find any use-cases or technical constraints that would "break" the design and / or prevent us from making Components and applicationsWe’ve noticed multiple discussion threads centered around the “application” vs. “component” responsibilities as well as dependency injection. We can clearly recognize the desire of shifting even more responsibilities to the components (ex. full DI configuration, including router configuration). But we believe that the concept of the "Angular application" is an important one and we want to preserve it -@alxhub wrote adetailed comment describing our thinking. Wedo hear loud and clear that the component / application distinction might not always be clear and we need to do better explaining and documenting the design. We also didn't plan to re-design the DI system as part of the "standalone" efforts. Angular always had two types of injectors (so-called "module" injector and "node" injector) and it continues to be the case with standalone components. Community feedbackWe've solicited feedback for some specific design questions in this RFC and your input was very valuable. Incorporating this feedback in our design, we intend to:
Frequently asked questionsFinally, we’ve identified some recurring questions and would like to highlight answers here. Import package for |
BetaWas this translation helpful?Give feedback.