NgModules consolidate components, directives, and pipes into cohesive blocks of functionality, each focused on a feature area, application business domain, workflow, or common collection of utilities.
An NgModule is a class marked by the @NgModule decorator. @NgModule takes a metadata object that describes how to compile a component's template and how to create an injector at runtime. It identifies the module's own components, directives, and pipes, making some of them public, through the exports property, so that external components can use them. @NgModule can also add service providers to the application dependency injectors.
Angular 14 is going to introduce an alternative way to write applications — Standalone components, directives, and pipes.
The term “standalone” refers to components, directives, or pipes that can be used independently of NgModule. Although you’ll still need to use the core and external NgModules, you probably won’t need to create new ones.
Let’s create an application without NgModules. First, we need to generate it using the angular-cli:
npx @angular/cli@next new ng14
The next step is to delete app.module.ts and replace the bootstrapModule() function in main.ts with bootstrapApplication():
import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
bootstrapApplication(AppComponent)
The bootstrapApplication() can take a list of providers that should be available to the root component and all its children:
import { importProvidersFrom } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { HttpClientModule } from '@angular/common/http'
bootstrapApplication(AppComponent, {
providers: [importProvidersFrom(HttpClientModule)]
}).catch(err => console.error(err));
The function extracts the providers from the provided module.
Now we need to change the AppComponent to be a standalone component. Let's set the standalone property to true:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
standalone: true,
styleUrls: ['./app.component.scss']
})
export class AppComponent {}
Now we can see the AppComponent’s template in our browser. As our component is standalone, we can use the new imports property. The imports property specifies the template dependencies of the component — those directives, components, and pipes it can use.
Standalone components can import other standalone components, directives, pipes, and existing NgModules. For example, we can create a standalone directive, and use it in our component:
npx ng g directive foo --standalone
import { Directive } from '@angular/core';
@Directive({
selector: '[appFoo]',
standalone: true
})
export class FooDirective {}
import { CommonModule } from '@angular/common';
import { FooDirective } from './foo.directive';
@Component({
selector: 'app-root',
template: `
<div appFoo *ngIf="bar">Foo</div>
`,
standalone: true,
imports: [FooDirective, CommonModule]
})
export class AppComponent {}
Let us add routing to the application.
const routes: Routes = [{
path: 'todos',
component: TodosPageComponent
}]
@Component({
selector: 'app-root',
template: `
<a routerLink="/todos">Todos</a>
<router-outlet></router-outlet>
`,
standalone: true,
imports: [RouterModule.forRoot(routes)],
styleUrls: ['./app.component.scss']
})
export class AppComponent {}
This isn’t possible because Angular doesn’t allow us to use ModuleWithProvider in a standalone component. Next, we might try using the new importProvidersFrom function in the component’s providers:
const routes: Routes = [{
path: 'todos',
component: TodosPageComponent
}]
@Component({
selector: 'app-root',
template: `
<a routerLink="/todos">Todos</a>
<router-outlet></router-outlet>
`,
standalone: true,
providers: importProvidersFrom(RouterModule.forRoot(routes)),
imports: [FooDirective, CommonModule],
styleUrls: ['./app.component.scss']
})
export class AppComponent {}
Using in-app navigation will work. The router will miss the first navigation. Initialization of the router should be performed in the bootstrap process:
bootstrapApplication(AppComponent, {
providers: [importProvidersFrom(RouterModule.forRoot(routes))]
}).catch(err => console.error(err));
The TodosPageComponent is eagerly loaded. Let’s change it to load lazily and add a TodoPageComponent:
import { Routes } from '@angular/router';
export const todosRoutes: Routes = [
{
path: 'todos',
title: 'Todos Page',
children: [
{
path: '',
loadComponent: () =>
import('./todos-page.component').then((m) => m.TodosPageComponent),
children: [
{
path: ':id',
loadComponent: () =>
import('./todo-page/todo-page.component').then(
(m) => m.TodoPageComponent
),
},
],
},
],
},
];
Instead of using loadChildren and passing a NgModule, we use the loadComponent property and pass a component. We can also declare providers for this Route and its children using the new providers property:
import { Routes } from '@angular/router';
export const todosRoutes: Routes = [
{
path: 'todos',
title: 'Todos Page',
providers: [
{
provide: 'Angular',
useValue: 'v14',
},
],
children: [
{
path: '',
loadComponent: () =>
import('./todos-page.component').then((m) => m.TodosPageComponent),
children: [
{
path: ':id',
loadComponent: () =>
import('./todo-page/todo-page.component').then(
(m) => m.TodoPageComponent
),
},
],
},
],
},
];
We can also pass an array of routes to loadChildren:
export const ROUTES: Route[] = [
{ path: 'child', component: ChildCmp},
]
{
path: 'parent',
loadChildren: () => import('./children').then(m => m.ROUTES),
}
Conclusion:
In coming days, Angular team may come up with Angular 14 update and new feature's. Stay tuned!
Top comments (3)
This gives me hope for the future iterations of Angular, but it is still not at the same level of flexibility of react and other frameworks. The component based dependency injection seems really nice, up until the documentation comes into play. So many shifts from the original pattern of Angular that moving to the new pattern will take quite some time for people to get used to it.
Their ngmodule system is why I left Angular 2 years ago. Won't go back now due to native Webcomponents.
Also, you might want to add "ts" to your code blocks so you receive syntax highlighting.