Say No to NgModule in Angular 14!
Tags: programming, javascript, angular, tutorial
Published: 2022-05-28 16:19:57

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!