[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.
-
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
NgModule
is 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,
NgModule
acts as a "unit of reasoning and reuse":NgModule
sNgModule
, etc.Given this central role of
NgModule
in 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
UserViewComponent
is authored like this:UserViewComponent
here assumes it will be able to injectUserViewService
. This assumption is usually safe because in ordinary usage, users who want to useUserViewComponent
don’t depend on it directly, but instead addUserViewModule
to theirNgModule.imports
.UserViewModule
brings with it the provider needed forUserViewService
, and so the component will work just fine.Attempting to instantiate
UserViewComponent
directly, however, risks violating this assumption. If the application hasn’t independently importedUserViewModule
somewhere in itsNgModule
hierarchy, 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:
NgModule
s 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
NgModule
has a significant impact on the developer experience:NgModule
, if it’s meant to be reused independently, orNgModule
hierarchy.bootstrapModule()
vsbootstrapComponent()
, orViewContainerRef.createComponent()
example above.NgModule
to understand the component’s dependencies.NgModule
context: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.declarations
until much later in their education;NgModules
, reducing the amount of code that needs to be written for typical development scenarios;NgModule
concept 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
NgModule
from 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
NgModule
for 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
NgModules
to 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, anNgModule
is imported, which contains exported components, directives and pipes as well as configured providers.Depending on things indirectly via an imported
NgModule
introduces subtle assumptions:configuration of the required dependency injection (DI) tokens: because the application is required to import the
NgModule
in order to use the component, directive or pipe, any providers declared within thatNgModule
are guaranteed to be available for injection. If the application were somehow able to skip theNgModule
and 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
NgModel
directive matches on an<input>
element like so:it also expects the collaborating
DefaultControlValueAccessor
directive to match on the same<input>
DOM element (through theinput
selector).The
FormsModule
(which exportsNgModel
) also exportsDefaultControlValueAccessor
.Both the
NgModel
and theDefaultControlValueAccessor
directives 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
NgModule
assume presence of a certain context (DI tokens and collaborating directives). AnNgModule
specifies this context.Standalone components, directives, and pipes
A standalone directive, component, or pipe is not declared in any existing
NgModule
, and:NgModule
);NgModule
.The
standalone
flag 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
standalone
flag 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: true
flag 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. Theimports
property on the decorator specifies the component's template dependencies — those directives, components, and pipes that can be used within its template:Interop with
NgModule
examplesStandalone components, directives and pipes can be imported by other standalone components, as well as by
NgModule
s:Here,
AppComponent
(which is declared inAppModule
and thus has its template managed byAppModule
) is given visibility ofExampleStandaloneComponent
via the import inAppModule
.Conversely, standalone components can also import existing
NgModule
s:In this example,
ExampleStandaloneComponent
uses theNgForOf
directive and theAsyncPipe
, both of which are made available by importingCommonModule
. This ability of importing existingNgModule
s is very important for the interoperability story — it assures that the large ecosystem of existingNgModule
s is usable as-is from standalone components.Ways to reason about standalone components
NgModule
described in this section. If you are new toAngular
or theNgModule
concept you can safely skip this part of the RFC and go directly to the "Use-cases and code examples" part.Virtual
NgModule
mental modelA standalone component, directive, or pipe can be considered as being self-declaring. They behave as if there was an
NgModule
which declared (and exported) the component in question (and only this one component). In practice thisNgModule
doesn'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"@NgModule
class in the final implementation.As previously mentioned, this "virtual"
NgModule
is not accessible to developers, and theExampleStandaloneComponent
class 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
NgModule
for a single component, directive, or pipe does not have to be written by a developer — it is "natively supported" by the framework.NgModule
or 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" / SCAMNgModule
s.Declarations
Declaring a standalone component, directive or pipe in an
NgModule
is an error reported at compilation time.ℹ️ Virtual
NgModule
analogy:A standalone component, directive or pipe was already declared in its own "virtual"
NgModule
and it is not possible to declare a component, directive or pipe in 2 differentNgModule
s.Imports and schemas
Since a standalone component takes on some responsibilities of a
NgModule
we need to extend the list of the properties available in the@Component
decorator. 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 theimports
from the@NgModule
schemas
- exact syntax and semantics of theschemas
from the@NgModule
The
imports
andschemas
properties on the@Component
annotation are allowed only in the presence of thestandalone: true
flag. The compiler will report an error ifimports
orschemas
property is present without the associatedstandalone: true
flag.ℹ️ Virtual
NgModule
analogy:Importing a standalone component/directive/pipe into either another standalone component, or into an
NgModule
, behaves as if its virtualNgModule
was 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
NgModule
sExisting
NgModule
s imported into a standalone component might contain providers.The providers of all
NgModule
s imported (directly or transitively) into a standalone component are "rolled up" and made available to otherNgModule
s or standalone components that import it in turn.This "rolling up" of providers continues until we reach a top-level
NgModule
(typically the applicationNgModule
but potentially a lazy-loadedNgModule
).This is actually how providers are handled in the
NgModule
imports graph today: Providers are not scoped to an instance of aNgModule
but rather are instantiated by an injector representing an accumulated set of all providers from the entire imports graph.ℹ️ Virtual
NgModule
analogy:The collection of providers can be illustrated on the following drawing:
The mechanism is equivalent to how providers are interpreted when traversing the
NgModule
imports 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
providers
Unlike providers that are "rolled up" from imported
NgModule
s, providers declared via theproviders
property 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
NgModule
or 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
NgModule
but still need a way of specifying their template dependencies. This is done with theimports
property of the@Component
decorator:It is also possible to directly depend on components, directives and pipes exported by existing
NgModule
s:The
@Component.imports
supports the same syntax and semantics as@NgModule.imports
. In practice it means that users could group several collaborating directives in anArray
and 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:
NgModule
s (no changes are required to those modules) - this means that standalone components can take advantage of the entire existing ecosystem of libraries exposed asNgModule
s;NgModule
" mental model we can think of the imports property as "importNgModule
"s here. There is really no distinction between importing an existingNgModule
and a standalone component, directive or pipe - we always import anNgModule
(a "real" or a "virtual" one);imports
property has the same syntax and semantics as the same property on the@NgModule
decorator. 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:
NgModule
only;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
NgModule
without 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
+DefaultValueAccessor
combination 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
NgModule
was 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. TheNgModule
context 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:
StandaloneComponent
can be lazy-loaded without any associatedNgModule
;Bootstrap
Angular developers need to create an
NgModule
in order to bootstrap even the simplest "Hello, World" application. In practice this means that theNgModule
concept 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:
NgModule
concept 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
createStandaloneComponent
method 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:
imports
vs. other namesThe current proposal uses the
imports
keyword to specify dependencies of a standalone component. This name was chosen for the following reasons:NgModule
" mental model (existing@NgModule
usesimports
and we plan to have the exact same semantics);import
keyword 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
imports
word can be confused with the JavaScript imports and / or@NgModule.imports
so we've also considered different names:deps
uses
Auto-importing the
CommonModule
or its partsOne of the open questions is the explicitness of the dependency on the
CommonModule
. It should be noted that with the current proposal theCommonModule
would 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
CommonModule
The
CommonModule
could 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 (
ngIf
andngFor
) and other items from theCommonModule
(like theasync
pipe) 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
CommonModule
into smaller modulesWe could consider breaking the
CommonModule
into 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
ControlFlowModule
containingNgIf
,NgFor
andNgSwitch
).Mark all the directives and pipes from the
CommonModule
asstandalone: true
To 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
CommonModule
into 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
NgModule
s in the future? Will these be deprecated / removed?The short term-answer is that
NgModule
s are not going away and not getting deprecated - you can continue to write and consume existingNgModule
s.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
NgModule
story in a backward compatible way.As of today
NgModule
s have many responsibilities, some of them being very useful (ex. grouping cooperating directives), some others having alternatives (ex. theproviders
property vs. tree-shakable providers) and others being outright confusing. As the general direction we want to reduce or eliminate sources of complexity in theNgModule
system by separating out some of its responsibilities into less tangled APIs that might eventually completely replaceNgModule
.We are very much aware that
NgModule
s 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
NgModule
works 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: true
flag?While it doesn't have to be the
standalone: true
flag, we need some syntax to mark components, directives and pipes as "standalone". Reasons:NgModule
and I will work correctly";standalone: false
tostandalone: 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
standalone
flag from theimports
presence?We discussed this in detail, and the consensus was that the explicitness of
standalone: true
is desirable for a few reasons:imports
only makes sense for components;imports
which shows thatstandalone
andimports
are 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 —imports
that are not used in a template will not be part of the generated code (and thus could be tree-shaken). The only difference withstandalone: true
is that the compiler will have an easier time figuring out what is potentially used in a template — it can simply inspect theimports
graph without going through the layer of indirection ofNgModule
s.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.imports
or indicate unnecessaryimports
based 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 new
command);standalone: true
, flipping the default value of thestandalone
property 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
NgModule
s (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:NgModule
s 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