Введение в модули Angular — корневой модуль (Root Module)

в 13:02, , рубрики: angular, javascript, TypeScript, Разработка веб-сайтов

Прим. перев.: для понимания данной статьи необходимо обладать начальными знаниями Angular: что такое компоненты, как создать простейшее SPA приложение и т.д. Если Вы не знакомы с данной темой, то рекомендую для начала ознакомиться с примером создания SPA приложения из оф. документации.

Вступление

@NgModuleдекоратор, добавленный в Angular 2. Из официальной документации следует, что @NgModule определяет класс, как модуль Angular. Модули Angular помогают разбивать приложение на части (модули), которые взаимодействуют между собой и представляют в конечном итоге целостное приложение. Иными словами, модуль — это упаковка или инкапсуляция части функционала приложения. Модули можно проектировать с учетом многократного использования, т.е. не зависящие от конкретной реализации приложения.

Обратите внимание, что если вы хотите реализовать lazy load в Вашем приложении, то необходимо использовать концепцию Angular Modules при проектировании приложения.

Корневой модуль (Root Module)

Корневой модуль в приложении Angular используется в качестве точки входа. Модуль — это класс, который декорирован при помощи @NgModule. Взглянем на стандартный код модуля (например, при создании нового проекта через ng new project-name генерируется такой AppModule:

// app.module.ts
@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule { }  

В качестве аргумента в декораторе @NgModule используется JavaScript объект.

В свойстве declarations мы объявляем компоненты, которые содержит наш модуль. В данном случае это компонент AppComponent. Компонентов может быть несколько, они объявляются через запятую (как в обычном JavaScript массиве).

declarations: [AppComponent]

Предположим, что наш компонент имеет селектор my-app. Когда в шаблоне HTML мы пишем <my-app></my-app> приложение загружает компонент.

Как происходит загрузка компонента в DOM?

AppModule сообщает браузеру, что необходимо встроить в DOM компонент AppComponent.

Каким образом AppModule добавляет компонент в DOM?

AppModule импортирует служебные модули Angular, например
BrowserModule, отвечающий за работу приложения в браузере. По сути это сервис, который взаимодействует с нашим кодом (например AppComponent) и API браузера. BrowserModule также включает в себя директивы NgIf и NgFor, который мы можем использовать в шаблонах компонентов.

image

AppModule выступает в роли связующего между данными, их представлением и браузером.

Компоновка приложения

Использование приложение Angular начинается с файла main.ts, более подробно читайте мануал.

// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';  
import { AppModule } from './app.module';  
platformBrowserDynamic().bootstrapModule(AppModule); 

В примере выше мы используем динамическую компиляцию (JIT — Just In Time). Однако в Angular можно реализовать AOT-компиляцию (Ahead of Time), однако это обширная тема и будет рассмотрена в другой статье.

Декларирование (объявление)

В наших приложения мы создаем свои компоненты, директивы и пайпы. Мы можем сообщить нашему приложению о том, какой функционал мы хотим добавить (компоненты, директивы и пайпы) путем перечисления их в свойстве declarations объекта, который является аргументом для декоратора @NgModule:

// app.module.ts
@NgModule({
  imports: [BrowserModule],
  declarations: [
    AppComponent,
    VehicleListComponent,
    VehicleSelectionDirective,
    VehicleSortingPipe
  ],
  bootstrap: [AppComponent],
})
export class AppModule { } 

После объявления компонентов мы можем использовать их внутри других компонентов через их селектор, который указывается в описании компонента.

Импортирование и вложенные модули

Мы импортируем BrowserModule в AppModule, который в свою очередь импортирует CommonModule, который и содержит директивы NgIf и NgFor, доступные в Angular из коробки. Импортируя CommonModule мы получаем доступ к функционалу, реализованный в данном модуле во всем приложении.

Но что если мы хотим использовать функционал, который необходим для реализации форм, например ngModel и http? Решение просто — нам достаточно импортировать данные модули в AppModule:

// app.module.ts
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  declarations: [
    AppComponent,
    VehicleListComponent,
    VehicleSelectionDirective,
    VehicleSortingPipe
  ],
  bootstrap: [AppComponent],
})
export class AppModule { }  

После того, как мы импортировали модуль FormsModule, мы можем использовать ngModel в шаблонах наших компонентов. А HttpModule позволяет нам отправлять или получать данные по HTTP протоколу.

Провайдеры

Часто в наших приложениях есть сервисы, которые мы хотим использовать в нескольких (или во всех) компонентах. Как предоставить всем компонентам доступ к сервису? Использовать Angular Modules.

Для этого необходимо добавить провайдер в корневой модуль, в нашем случае AppModule. В Angular добавление зависимостей реализовано при помощи паттерна Dependency Incetion. Когда мы добавили провайдер в корневой модуль AppModule, он будет доступен в любой части приложения.

В качестве наиболее наглядного примера провайдера, который нужен в нашем приложении везде — сервис авторизации (Logger Service). Еще наше приложение должно показывать список активных пользователей, поэтому нам необходим VehicleService для извлечения списка активных пользователей, обращаясь к API, используя http, который мы можем использовать благодаря модулю HttpModule. Его мы импортировали в наше приложение раньше.

// logger.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class LoggerService {  
  log = (msg: string) => {
    console.log(msg);
  };
}

// vehicle.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class VehicleService {  
  getVehicles = () => [
    { id: 1, name: 'X-Wing Fighter' },
    { id: 2, name: 'Tie Fighter' },
    { id: 3, name: 'Y-Wing Fighter' }
  ];
}

Когда мы представили эти сервисы в корневом модуле AppModule они доступны в компонентах приложения:

// app.module.ts
@NgModule({
  imports: [BrowserModule, FormsModule, HttpModule],
  declarations: [
    AppComponent,
    VehicleListComponent,
    VehicleSelectionDirective,
    VehicleSortingPipe
  ],
  providers: [
    LoggerService,
    VehicleService,
    UserProfileService
  ],
  bootstrap: [AppComponent],
})
export class AppModule { }  

Опишем, как это работает. Мы добавили в конструктор нашего компонента создание объекта провайдера, выглядит это примерно так:

// some.component.ts
@Component({
    selector: 'some-component',
    template: '<h1>Some component {{ service.data }}</h1>'
    styleUrls: ['./path/to/style.css']
})

export class SomeComponent implements OnInit{
    construcotr (private service: Service){} //Создание объекта сервиса
    // Основной код компонента
}

При создании экземпляра компонента (например, когда мы открыли страницу, где используется компонент) одновременно создается объект сервиса, описанный в свойстве providers (см. листинг app.module.ts выше) и Angular начинает искать класс этого сервиса в родительских компонентах. Т.к. мы не объявляли провайдер в компонентах, в конечном итоге Angular найдет этот сервис в корневом модуле AppModule. Следует отметить, что провайдер в Angular реализуют паттерн Singleton, так что в приложении может быть создан только один экземпляр класса сервиса. В нашем случае, если экземпляр сервиса уже был где-то создан, то компонент будет использовать его, если нет, то создаст объект сервиса.

Заключение

Провайдеры, на мой взгляд, одна из самых интересных и сложных концепций модульной системы Angular. Основное правило использования провайдеров (сервисов) — создать экземпляр сервиса в корневом модуле и передавать по запросу этот экземпляр в другие части (компоненты) приложения.

Автор: Алексей Барышников

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js