Скажите «нет» NgModule в Angular 14!

NgModules объединяет компоненты, директивы и трубы в целостные функциональные блоки, каждый из которых ориентирован на определенную область возможностей, бизнес-область приложения, рабочий процесс или общую коллекцию утилит.

NgModule — это класс, отмеченный декоратором @NgModule. @NgModule принимает объект метаданных, который описывает, как скомпилировать шаблон компонента и как создать инжектор во время выполнения. Он идентифицирует собственные компоненты, директивы и трубы модуля, делая некоторые из них публичными через свойство exports, чтобы внешние компоненты могли их использовать. @NgModule также может добавлять поставщиков услуг к инжекторам зависимостей приложения.

Angular 14 собирается представить альтернативный способ написания приложений — автономные компоненты, директивы и трубы.

Термин «автономный» относится к компонентам, директивам или трубам, которые можно использовать независимо от NgModule. Хотя вам все равно придется использовать основные и внешние NgModules, вам, вероятно, не придется создавать новые.

Давайте создадим приложение без NgModules. Сначала нам нужно сгенерировать его с помощью angular-cli:

npx @angular/cli@next new ng14

Следующим шагом будет удаление app.module.ts и замена функции bootstrapModule() в main.ts на 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)
Вход в полноэкранный режим Выход из полноэкранного режима

Функция bootstrapApplication() может принимать список провайдеров, которые должны быть доступны корневому компоненту и всем его дочерним компонентам:

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));
Войти в полноэкранный режим Выйти из полноэкранного режима

Функция извлекает провайдеры из предоставленного модуля.

Теперь нам нужно изменить AppComponent, чтобы он стал самостоятельным компонентом. Установим свойство standalone в true:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  standalone: true,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {}
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь мы можем видеть шаблон AppComponent в нашем браузере. Поскольку наш компонент является автономным, мы можем использовать новое свойство imports. Свойство imports определяет зависимости шаблона компонента — те директивы, компоненты и трубы, которые он может использовать.

Автономные компоненты могут импортировать другие автономные компоненты, директивы, трубы и существующие NgModules. Например, мы можем создать отдельную директиву и использовать ее в нашем компоненте:

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 {}
Ввести полноэкранный режим Выход из полноэкранного режима

Давайте добавим маршрутизацию в приложение.

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 {}
Войти в полноэкранный режим Выход из полноэкранного режима

Это невозможно, поскольку Angular не позволяет нам использовать ModuleWithProvider в отдельном компоненте. Далее мы можем попробовать использовать новую функцию importProvidersFrom в провайдерах компонента:

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 {}
Вход в полноэкранный режим Выйти из полноэкранного режима

Использование навигации внутри приложения будет работать. Маршрутизатор пропустит первую навигацию. Инициализация маршрутизатора должна выполняться в процессе bootstrap:

bootstrapApplication(AppComponent, {
  providers: [importProvidersFrom(RouterModule.forRoot(routes))]
}).catch(err => console.error(err));
Войти в полноэкранный режим Выход из полноэкранного режима

TodosPageComponent загружается с нетерпением. Давайте изменим его на ленивый и добавим 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
              ),
          },
        ],
      },
    ],
  },
];
Вход в полноэкранный режим Выход из полноэкранного режима

Вместо использования loadChildren и передачи NgModule мы используем свойство loadComponent и передаем компонент. Мы также можем объявить провайдеров для этого Route и его дочерних компонентов с помощью нового свойства providers:

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
              ),
          },
        ],
      },
    ],
  },
];
Войти в полноэкранный режим Выйти из полноэкранного режима

Мы также можем передать массив маршрутов в loadChildren:

export const ROUTES: Route[] = [
  { path: 'child', component: ChildCmp},
]
Войти в полноэкранный режим Выйти из полноэкранного режима
{
  path: 'parent',
  loadChildren: () => import('./children').then(m => m.ROUTES),
}
Войти в полноэкранный режим Выход из полноэкранного режима

Заключение:

В ближайшие дни команда Angular может представить обновление Angular 14 и новые возможности. Следите за новостями!

Оцените статью
Procodings.ru
Добавить комментарий