[Complete] RFC: Standalone components, directives and pipes - making Angular's NgModules optional #43784
Replies: 70 comments 146 replies
-
|
Thanks for the detailed explanation and examples. Looks good to me Update (12/06/2021): Okay, after reading through comments, RFC (again), and based on my experience from other frameworks. Here are my thoughts
|
Beta Was this translation helpful? Give feedback.
-
|
This proposal looks like a great way to remove a lot of boilerplate ngModule code. How would lazy loading / bundling work if everything was heirachially declared through component imports? Would the compiler/optimiser make good choices? |
Beta Was this translation helpful? Give feedback.
-
|
Could we have a setting in angular.json that sets/overrides the default standalone value? |
Beta Was this translation helpful? Give feedback.
-
|
Tooling section could mention possible migration schematics. Maybe language service should be leveraged during the migration to collect correct list of |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
|
This is a great proposal and a great step in the right direction.
@Component({
imports: [
CommonModule,
FormsModule,
MatButtonModule,
MatIconModule,
MatToolbarModule,
MatMenuModule,
],
standalone: true,
selector: 'my-comp',
templateUrl: './template.html',
stylesUrl: ['./styles.css'],
})Being able to import these from a constant in a separate file is good, however, it still means you're looking elsewhere for the dependencies, albeit more explicitly.
If a component states that it is @Component({
providedIn: 'root',
selector: 'hello-world',
template: '<h1>Hello World</h1>'
styles: ['h1 { color: red; }']
})
export class HelloWorldComponent { }
@Component({
selector: 'app',
template: '<hello-world></hello-world>'
})
export class AppComponent { }
bootstrapComponent(AppComponent);And if you wanted to include the component in a particular NgModule, you simply replace the
// difficult to reason about
declarations: [AComponent],
imports: [
CommonModule,
FormsModule,
BComponent,
MatButtonModule
]
// easier to reason about and helps separate out that BComponent is self-contained
// whereas AComponent needs the Modules in the imports
declarations: [AComponent],
uses: [BComponent],
imports: [
CommonModule,
FormsModule,
MatButtonModule
]
|
Beta Was this translation helpful? Give feedback.
-
|
Great proposal π. +1 for Auto-import the CommonModule. I think we can accept a little bit of magic if it means we won't have to write
-1 for Edit: How will this affect @angular/elements? Edit2: How will this affect routing? |
Beta Was this translation helpful? Give feedback.
-
|
Great stuff! I would stick to the name imports. deps would be the only abreviation in the decorator. |
Beta Was this translation helpful? Give feedback.
-
|
I think this is a phenomenal proposal and am most excited for the ability to easily lazy load components at runtime and have them be split into their own bundle! My preference with regards to the how In the long run, I think splitting things like Thanks for all of your hard work on this proposal! |
Beta Was this translation helpful? Give feedback.
-
|
Great proposal! I am really looking forward to this. Some thoughts, questions, and feedback:
A big thank you for everyone's effort! I can't wait to see this sorted out and come to reality! π |
Beta Was this translation helpful? Give feedback.
-
|
Thanks a lot for this great RFC! Some remarks:
|
Beta Was this translation helpful? Give feedback.
-
|
One capability that seems to be lost with standalone components, unless I'm missing something, is encapsulation, i.e. the ability to make some components only usable within a specific feature module/component. As far as I understand, as soon as a component is standalone, every other component/module of the application is able to import and use it, even though it has been designed to be used only as a part of another component or feature module. This is possible, and IMHO, quite useful, with modules, which can declare components and not export them, to keep them private, and thus easily modifiable/deletable with an impact limited to their declaring module. It would maybe be a good idea to be able to specify the authorized scope of a component, i.e. mark it as usable only in the template of some other components, or importable by only some other modules. |
Beta Was this translation helpful? Give feedback.
-
|
Maybe it would be nice to be able to opt-in to auto-importing |
Beta Was this translation helpful? Give feedback.
-
|
Thoughts:
I wonder about this, since this assumes you have access to the source code, which is the case for code you own but not dependencies. One thought might be mirroring the |
Beta Was this translation helpful? Give feedback.
-
|
Awesome feature, this will make applications much simpler. No more one module per component in reusable components :) About the auto import of CommonModule, I'd prefer it to be explicitly set or, as other already mentioned, an opt-in config in the angular.json |
Beta Was this translation helpful? Give feedback.
-
Lazy Side EffectsIn order to implement feature likes ngrx's In the following example, (if everything is eager loaded), const SIDE_EFFECT = new InjectionToken('side effect');
@NgModule()
export class SideEffectModule {
constructor(@Inject(SIDE_EFFECT) fns) {
for (const fn of fns) {
fn();
}
}
static forFeature({ sideEffect }): ModuleWithProviders<SideEffectModule> {
return {
ngModule: SideEffectModule,
providers: [
{
provide: SIDE_EFFECT,
useValue: sideEffect,
multi: true,
},
],
};
}
}
@Component({
standalone: true,
imports: [
SideEffectModule.forFeature({
sideEffect: sayHello
})
],
...
})
export class HelloComponent {}
function sayHello() {
console.log('Hello!')
}How can we achieve the same result without modules and without changing anything in |
Beta Was this translation helpful? Give feedback.
-
|
Hello! First of all, this idea is amazing! Something that I want to know is if you are planning to support a "ComponentWithProviders" feature to configure standalone components. |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
|
This is a really cool and useful feature. Thanks and Kudos! |
Beta Was this translation helpful? Give feedback.
-
|
This is a very interesting and well-documented proposal, congrats and many thanks to the writer π π π I believe it goes in the good direction, some of the most confusing parts of Angular are related to NgModule (lazy loading, transitive dependencies, routing issues, DI, etc.). It would improve developer experience, reuse and sharing specially for simple GUI componentes, and one big point to me is that it could make testing easier and with less bolierplate. Regarding the feedback solicited:
My two cents βοΈ |
Beta Was this translation helpful? Give feedback.
-
|
While we think about naming of things and how all of this will be documented, will the group of components, directives, and pipes still be referred to as declarables? Can they still be called declarables if they may not be declared in NgModule anymore? @Component{
selector: ...,
standalone: true,
uses: [declarables],
...
}
export class ... |
Beta Was this translation helpful? Give feedback.
-
|
As someone who's been using SCAMs for some time now, standalone components make a lot of sense and will be awesome to have. As for auto-importing |
Beta Was this translation helpful? Give feedback.
-
|
An idea to give the option to add |
Beta Was this translation helpful? Give feedback.
-
|
I'm looking forward to seeing this proposal implemented in my projects π Here's my solicited feedback:
|
Beta Was this translation helpful? Give feedback.
-
|
how about @standalone() |
Beta Was this translation helpful? Give feedback.
-
|
I forgot to say: personally I would prefer explicit imports for Can't wait until the |
Beta Was this translation helpful? Give feedback.
-
|
This looks awesome. This can't happen quickly enough. Double thumbs up from me. |
Beta Was this translation helpful? Give feedback.
-
|
Nice article. What if we forget about NgModule al together and let the compiler detect components and directives from its template and locate and use them from sources? It would greatly simplify the Angular mental model and ease the pain for newcomers. |
Beta Was this translation helpful? Give feedback.
-
|
Are there any chances that this will be included into the Angular v14 release? |
Beta Was this translation helpful? Give feedback.
-
|
tl;dr; thank you for all the feedback - our intention is to proceed with the design described in this RFC. First of all we would like to thank everyone who commented on, or otherwise engaged in the discussion on this RFC. We had over 140 comments from more than 60 people on this RFC alone plus additional conversations through other channels (social media, meetups, conferences). We were blown away by the quality and depth of the discussion. Thank you! Based on all the comments and feedback we believe that the design was well understood and received. Our interpretation is that the Angular community supports our intention of moving the framework in the proposed direction. And critically, we haven't seen any use-cases or technical constraints that would "break" the design. We'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:
In your feedback, you raised several important questions that need additional design consideration:
Based on your feedback, we are confident enough in the core design of standalone components to proceed with implementation, but we will also embark on designing additional APIs and functionality when needed. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Author: Pawel Kozlowski
Contributors: Alex Rickabaugh, Andrew Kushnir, Igor Minar, Minko Gechev, Pete Bacon Darwin
Area: Angular Framework
Posted: October 8, 2021
Status: Complete - outcome summary linked here.
The goal of this RFC is to validate the design with the community, solicit feedback on open questions, and enable experimentation via a non-production-ready prototype included in this proposal.
Motivation
NgModuleis currently one of the core concepts in Angular. Developers new to Angular need to learn about this concept before creating even the simplest possible "Hello, World" application.More importantly,
NgModuleacts as a "unit of reasoning and reuse":NgModulesNgModule, etc.Given this central role of
NgModulein Angular it is hard to reason about components, directives and pipes in isolation.Dynamic component creation example
The following example, which dynamically renders a component, contains a subtle, yet critical, problem and is not guaranteed to work at runtime as a result!:
Suppose
UserViewComponentis authored like this:UserViewComponenthere assumes it will be able to injectUserViewService. This assumption is usually safe because in ordinary usage, users who want to useUserViewComponentdonβt depend on it directly, but instead addUserViewModuleto theirNgModule.imports.UserViewModulebrings with it the provider needed forUserViewService, and so the component will work just fine.Attempting to instantiate
UserViewComponentdirectly, however, risks violating this assumption. If the application hasnβt independently importedUserViewModulesomewhere in itsNgModulehierarchy, the needed provider wonβt be available at runtime, and dynamic creation will fail.Some components do not have such dependencies β they donβt rely on configuration provided in their
NgModuleβ and can be used directly in this manner. Many components, however, do rely on the context provided by theirNgModule, either its providers, or by expecting certain other components or directives to also be present in the same template.Components need NgModules
This may seem like an implementation detail of specific components, but in fact it illustrates a fundamental property of the framework:
NgModules are the smallest reusable building blocks in Angular, not components.Angular is one of the only web frameworks where components are not the βunits of reuseβ.
Having Angular conceptually centered around
NgModulehas a significant impact on the developer experience:NgModule, if itβs meant to be reused independently, orNgModulehierarchy.bootstrapModule()vsbootstrapComponent(), orViewContainerRef.createComponent()example above.NgModuleto understand the componentβs dependencies.NgModulecontext:Main benefits of this proposal
Move Angular in a direction where components, directives, and pipes play a more central role, are self-contained and can be safely imported / used directly.
The mental model shift is the main motivation of this proposal, but there are additional benefits of the reduced conceptual surface (fewer things to learn) and API surface (less code to write).
All these benefits combined should make Angular:
Goals and non-goals
Goals
NgModule:NgModule.declarationsuntil much later in their education;NgModules, reducing the amount of code that needs to be written for typical development scenarios;NgModuleconcept and API is not needed at all, and as such doesn't need to be learned / mastered.NgModule-based applications.NgModule-based counterparts;NgModule-based counterparts;Non-Goals
This proposal is not trying to remove the concept of a
NgModulefrom Angular β it is rather making it optional for typical application development tasks.At the same time we believe that it paves the path towards greatly reducing the role of
NgModulefor typical development scenarios β to the point that some time in the future it would be possible and reasonable for us to consider removing it altogether.Proposal
Current state
In Angular today, developers use
NgModulesto manage dependencies. When one component needs to make use of another component, directive, pipe or a provider (whether from within the same application, or from a third-party library on NPM) the dependency is not referenced directly. Instead, anNgModuleis imported, which contains exported components, directives and pipes as well as configured providers.Depending on things indirectly via an imported
NgModuleintroduces subtle assumptions:configuration of the required dependency injection (DI) tokens: because the application is required to import the
NgModulein order to use the component, directive or pipe, any providers declared within thatNgModuleare guaranteed to be available for injection. If the application were somehow able to skip theNgModuleand depend on a component directly, there is no guarantee that the DI system would be correctly configured and be able to instantiate the component;declarations of collaborating directives: a directive may require other directives to also match where it is used, even if the end user isnβt aware of their existence.
Collaborating directives example
When the
NgModeldirective matches on an<input>element like so:it also expects the collaborating
DefaultControlValueAccessordirective to match on the same<input>DOM element (through theinputselector).The
FormsModule(which exportsNgModel) also exportsDefaultControlValueAccessor.Both the
NgModeland theDefaultControlValueAccessordirectives must be active on the element for[(ngModel)]to function properly.Most Forms users are entirely unaware of this mechanism.
Generally speaking components, directives or pipes declared in a
NgModuleassume presence of a certain context (DI tokens and collaborating directives). AnNgModulespecifies this context.Standalone components, directives, and pipes
A standalone directive, component, or pipe is not declared in any existing
NgModule, and:NgModule);NgModule.The
standaloneflag is used to mark the component, directive or pipe as "standalone". It is a property of a metadata object of the relevant decorator (@Component,@Directive, or@Pipe).βΉοΈ Adding the
standaloneflag is a signal that components, directives, or pipes are independently usable. Such components, directives, or pipes don't depend on any "intermediate context" of aNgModule.Simple example
Let's examine a simple example:
In
HelloStandaloneComponent, thestandalone: trueflag marks the component as standalone. This makes it obvious to the reader and the tooling that this component is self-contained:NgModule.Dependencies example
Since a standalone component has no association with an
NgModule, we need a different mechanism of specifying template dependencies. Theimportsproperty on the decorator specifies the component's template dependencies β those directives, components, and pipes that can be used within its template:Interop with
NgModuleexamplesStandalone components, directives and pipes can be imported by other standalone components, as well as by
NgModules:Here,
AppComponent(which is declared inAppModuleand thus has its template managed byAppModule) is given visibility ofExampleStandaloneComponentvia the import inAppModule.Conversely, standalone components can also import existing
NgModules:In this example,
ExampleStandaloneComponentuses theNgForOfdirective and theAsyncPipe, both of which are made available by importingCommonModule. This ability of importing existingNgModules is very important for the interoperability story β it assures that the large ecosystem of existingNgModules is usable as-is from standalone components.Ways to reason about standalone components
NgModuledescribed in this section. If you are new toAngularor theNgModuleconcept you can safely skip this part of the RFC and go directly to the "Use-cases and code examples" part.Virtual
NgModulemental modelA standalone component, directive, or pipe can be considered as being self-declaring. They behave as if there was an
NgModulewhich declared (and exported) the component in question (and only this one component). In practice thisNgModuledoesn't exist (or is not made accessible to developers), and can be thought of as "virtual".Considering an example standalone component:
This component will behave as if it was declared and exported from a "virtual"
NgModule:Please note that we are using the same name (
ExampleStandaloneComponent) to indicate that a standalone component takes on some responsibilities of a@NgModule. It is also a hint that we will not generate a "virtual"@NgModuleclass in the final implementation.As previously mentioned, this "virtual"
NgModuleis not accessible to developers, and theExampleStandaloneComponentclass can be used in its place throughout Angular.SCAM pattern mental model
Another way of thinking about standalone components, directives and pipes is using the analogy of a single-component Angular module (so-called SCAM pattern popularized by @LayZeeDK). With this proposal an
NgModulefor a single component, directive, or pipe does not have to be written by a developer β it is "natively supported" by the framework.NgModuleor the SCAM pattern is just a "thinking tool" to help us reason about the design described in this RFC. For performance and maintainability reasons, the actual implementation of this proposal will very likely not end up generating or using "virtual" / SCAMNgModules.Declarations
Declaring a standalone component, directive or pipe in an
NgModuleis an error reported at compilation time.βΉοΈ Virtual
NgModuleanalogy:A standalone component, directive or pipe was already declared in its own "virtual"
NgModuleand it is not possible to declare a component, directive or pipe in 2 differentNgModules.Imports and schemas
Since a standalone component takes on some responsibilities of a
NgModulewe need to extend the list of the properties available in the@Componentdecorator. More formally we add the following properties with the same syntax and semantics as if placed in an@NgModule:imports- exact syntax and semantics of theimportsfrom the@NgModuleschemas- exact syntax and semantics of theschemasfrom the@NgModuleThe
importsandschemasproperties on the@Componentannotation are allowed only in the presence of thestandalone: trueflag. The compiler will report an error ifimportsorschemasproperty is present without the associatedstandalone: trueflag.βΉοΈ Virtual
NgModuleanalogy:Importing a standalone component/directive/pipe into either another standalone component, or into an
NgModule, behaves as if its virtualNgModulewas imported instead. This means that the single exported standalone component, directive or pipe is added to the compilation scope of the importingNgModule.Providers from imported
NgModulesExisting
NgModules imported into a standalone component might contain providers.The providers of all
NgModules imported (directly or transitively) into a standalone component are "rolled up" and made available to otherNgModules or standalone components that import it in turn.This "rolling up" of providers continues until we reach a top-level
NgModule(typically the applicationNgModulebut potentially a lazy-loadedNgModule).This is actually how providers are handled in the
NgModuleimports graph today: Providers are not scoped to an instance of aNgModulebut rather are instantiated by an injector representing an accumulated set of all providers from the entire imports graph.βΉοΈ Virtual
NgModuleanalogy:The collection of providers can be illustrated on the following drawing:
The mechanism is equivalent to how providers are interpreted when traversing the
NgModuleimports graph today - the only difference here is that the imports graph can contain a mix of "real"NgModule(hand-written by Angular developers) and "virtual" ones (representing standalone components, directives or pipes).Component
providersUnlike providers that are "rolled up" from imported
NgModules, providers declared via theprovidersproperty on a standalone component keep the same semantics as a non-standalone component.In practice this means such providers are defined on the node injector associated with the host node of the component and not an
NgModuleor top level application injector.Instead, to ensure a provider is added to a top level injector, a standalone component, directive or pipe should use tree-shakeable providers - for example
@Injectable({providedIn: 'root'}).Unlike a real
NgModule, a standalone component (and its "virtual"NgModule) can NOT specify providers to be instantiated on a top level injector.Use-cases and code examples
This section goes over several practical use-cases and provides code examples for each use-case. Here we don't introduce any new concepts nor APIs but rather "derive" them from the fundamental design choices outlined so far.
@component / @directive / @pipe APIs
Standalone components, directives and pipes
A component, directive, or pipe can be marked as "standalone". This clearly signals that the βstandaloneβ entity is not declared in any NgModule and thus is not part of any
NgModule.Example component:
Example directive:
Example pipe:
Notable points:
Standalone components using custom elements
Standalone components can use custom elements in a template by specifying an appropriate element name validation schema, ex.:
Standalone component with template dependencies
Standalone components are not declared in any
NgModulebut still need a way of specifying their template dependencies. This is done with theimportsproperty of the@Componentdecorator:It is also possible to directly depend on components, directives and pipes exported by existing
NgModules:The
@Component.importssupports the same syntax and semantics as@NgModule.imports. In practice it means that users could group several collaborating directives in anArrayand use such group in@Component.imports:This technique makes it possible to create groups of collaborating standalone components, directives and pipes (ones that should match together on a given element) without needing an
NgModule.Notable points:
NgModules (no changes are required to those modules) - this means that standalone components can take advantage of the entire existing ecosystem of libraries exposed asNgModules;NgModule" mental model we can think of the imports property as "importNgModule"s here. There is really no distinction between importing an existingNgModuleand a standalone component, directive or pipe - we always import anNgModule(a "real" or a "virtual" one);importsproperty has the same syntax and semantics as the same property on the@NgModuledecorator. Most notably, the value of this property must be statically analyzable.Libraries
With the introduction of standalone components, directives and pipes we open up a debate on changes to how libraries should be architected in response to this proposal. Essentially a library author will have the following choices:
NgModuleonly;NgModule(for applications that prefer to use them);Regardless of the final recommendation and the exact choice done by the library author it is important to note that all the options listed above are possible.
To start with, library authors can continue to publish the existing
NgModulewithout any changes. Those are guaranteed to work as-is. This is also the best choice for libraries composed of collaborating directives that must match on the same element (theNgModel+DefaultValueAccessorcombination is a good example).Then, a library might choose to expose standalone components, directives and pipes but still create a
NgModule:Finally, a library author might choose to export exclusively a set of standalone components, directives and pipes. Such a set would be usable from standalone components and could be imported into any
NgModule(if an application chooses to use them). This is a good choice when a library consists of independent and non-cooperating components, directives and pipes.Notable points:
Other APIs using NgModule
The "standalone" concept makes it possible to simplify the existing APIs: generally speaking it should be possible to use a standalone component, directive or pipe in places where an
NgModulewas previously required.This section contains examples of APIs that could be simplified. The intention is to show what might be possible rather than fully design or commit to those APIs.
Components: lazy loading and instantiation
At present, when lazy loading components in Angular, developers have to first lazy-load the component's
NgModule, and then use it to instantiate the component. TheNgModulecontext is required to ensure that declared components have their compilation scope and providers setup correctly.With the "standalone" option we've got a guarantee that a component is "self-contained" and we can start using standalone components as a lazy-loading boundary:
Notable points:
StandaloneComponentcan be lazy-loaded without any associatedNgModule;Bootstrap
Angular developers need to create an
NgModulein order to bootstrap even the simplest "Hello, World" application. In practice this means that theNgModuleconcept needs to be taught and learned while getting started with Angular.With the "standalone" proposal implemented, we could introduce an alternative bootstrap API where a standalone component is directly used as a root component:
Notable points:
NgModuleconcept and the associated APIs.Router
Since a standalone component can be lazy loaded and dynamically instantiated we could modify router APIs to allow standalone, lazy-loaded leaf routes without the need for child route configuration or an
NgModule:TestBed
While testing standalone components, we could avoid the need for a dedicated testing
NgModule. In the simplest possible case a test could look like:In case one needs to override components / directives in the component under test the
createStandaloneComponentmethod could take an optional argument with overrides:Possible API choices - soliciting community feedback
While brainstorming and designing the APIs presented here there were multiple times where we had hard time deciding between multiple, equally valid options. Here we would like present alternatives considered and solicit community feedback to choose the best option.
Syntax:
importsvs. other namesThe current proposal uses the
importskeyword to specify dependencies of a standalone component. This name was chosen for the following reasons:NgModule" mental model (existing@NgModuleusesimportsand we plan to have the exact same semantics);importkeyword to denote the "bring something existing into a scope" operation and a standalone component brings an existing component, directive or pipe into its template compilation scope.At the same time we hear the feedback where the
importsword can be confused with the JavaScript imports and / or@NgModule.importsso we've also considered different names:depsusesAuto-importing the
CommonModuleor its partsOne of the open questions is the explicitness of the dependency on the
CommonModule. It should be noted that with the current proposal theCommonModulewould have to be imported into the majority of non-trivial standalone components:While this is consistent with the mental model presented so far, there are alternative approaches.
The main trade off of the following variants is explicitness (at the cost of verbosity) and code succinctness (at the cost of introducing more "magic" to the system).
Our general preference is to lean towards explicitness and fine-granular dependencies with developer experience improved via tooling (compiler errors) and IDE auto-completion (via language service).
Auto-import the
CommonModuleThe
CommonModulecould be an implicit import to all standalone components. In other, words all standalone components would behave as if they always imported theCommonModule:Here the component can use control flow (
ngIfandngFor) and other items from theCommonModule(like theasyncpipe) without an explicit import. This reduces verbosity / boilerplate code but makes Angular less explicit and "more magical".Since we would like Angular to become more explicit and easier to reason about, this option is not preferred by the team.
Break the
CommonModuleinto smaller modulesWe could consider breaking the
CommonModuleinto smaller ones (ex.ControlFlowModule,AsyncModule,I18nModule, ...) so users need only import parts that are actually used in a template. This would make template dependencies more fine-grained and explicit.Additionally we could consider auto-importing a smaller module (ex. only auto-import the
ControlFlowModulecontainingNgIf,NgForandNgSwitch).Mark all the directives and pipes from the
CommonModuleasstandalone: trueTo have even more fine-grained control over what is being visible to a template scope we could turn all the directives and pipes from the
CommonModuleinto standalone ones. This would make it possible to import them individually in the@Component.imports(with the aid of IDE auto-completion powered by the Angular language service):Again, we could decide to auto-import certain directives / pipes.
FAQ
The future
What will happen with
NgModules in the future? Will these be deprecated / removed?The short term-answer is that
NgModules are not going away and not getting deprecated - you can continue to write and consume existingNgModules.The long-term answer is: we will monitor the adoption of standalone components, directives, and pipes, look into community feedback, and work on simplifying the overall
NgModulestory in a backward compatible way.As of today
NgModules have many responsibilities, some of them being very useful (ex. grouping cooperating directives), some others having alternatives (ex. theprovidersproperty vs. tree-shakable providers) and others being outright confusing. As the general direction we want to reduce or eliminate sources of complexity in theNgModulesystem by separating out some of its responsibilities into less tangled APIs that might eventually completely replaceNgModule.We are very much aware that
NgModules play a very central role in Angular applications today, so we will move very carefully in this area.Should I convert my apps / libraries to standalone components, directives and pipes?
We hope that the simplifications offered by this proposal will result in tangible benefits that will incentivise developer adoption. If you see benefits of using standalone components, directives and pipes β by all means please use them. If
NgModuleworks for you - continue to use them.Initially we are planning on providing only minimal guidance on the "standalone" vs.
NgModule-based approach (mostly around library publishing). We will continue to monitor the community's feedback and update guidance as we gather more data and learnings from real-life usage.Syntax
Do we need the
standalone: trueflag?While it doesn't have to be the
standalone: trueflag, we need some syntax to mark components, directives and pipes as "standalone". Reasons:NgModuleand I will work correctly";standalone: falsetostandalone: true. Thus enabling safe, incremental, and automatable migration.Also check the discussion in the "Standalone components directives and pipes" section
Couldnβt we derive the
standaloneflag from theimportspresence?We discussed this in detail, and the consensus was that the explicitness of
standalone: trueis desirable for a few reasons:importsonly makes sense for components;importswhich shows thatstandaloneandimportsare 2 different concepts;Performance
Does this proposal affect the ability to tree shake components, directives, pipes and providers?
There should be no change in what Angular compiler can tree-shake.
As of today the Angular compiler needs to understand what are the components, directives and pipes used in a component's template. The generated code has only references to what is being used in a template, regardless of how many components, directives or pipes are available in a
NgModule. With the introduction of the standalone components, directives and pipes the generated code won't change βimportsthat are not used in a template will not be part of the generated code (and thus could be tree-shaken). The only difference withstandalone: trueis that the compiler will have an easier time figuring out what is potentially used in a template β it can simply inspect theimportsgraph without going through the layer of indirection ofNgModules.Tooling
What type of tooling can we expect if this proposal is implemented?
We want to have standalone components, directives and pipes well integrated into the existing Angular tooling. More specifically:
@Component.importsor indicate unnecessaryimportsbased on what is being used in a template. Those are just examples but generally speaking we expect the language service to be fully integrated with the "standalone" way of doing things;ng newcommand);standalone: true, flipping the default value of thestandaloneproperty etc.);Documentation and learning resources
How do we teach this?
With the introduction of the standalone components, directives and pipes Angular developers will have a choice of structuring their applications around
NgModules (as of today) or around standalone components (based on the proposal in this RFC). This choice will be reflected in the different learning journeys covered in our documentation:NgModules we will create a "moving to standalone components, directives and pipes" learning journey - there we will be able to compare and contrast the "standalone" approach with theNgModule-centered approach;NgModule-based applications and "fully standalone" applications.We will work on the exact documentation update plan as well as closely collaborate with Angular trainers / educators based on the RFC's feedback.
Additional resources
Beta Was this translation helpful? Give feedback.
All reactions