Статья на английском языке README.md
Обычно приложение Angular для корпоративного клиента создается по схеме монолита. То есть одно большое приложение. Со временем, функциональность такого приложения увеличивается. При чем часто заказчик ставит сжатые сроки разработки. В результате приложение Angular имеет огромный размер. Попытка разделить такое приложение на отдельные модули заканчивается неудачей. Это происходит по тому, что модули сильно связаны друг с другом. По этой же причине, данное приложение становится сложно сопровождать - тратиться много времени.
Выход из данной ситуации - создавать приложение по модульном принципу. То есть функционал главного меню выносится в отдельные независимые модули. И эти модули должны подгружаться ленивой загрузкой. И при старте приложения будет загружаться только требуемый модуль. Время загрузки сокращается и не зависит от общего количества модулей. Если пользователь перейдет на следующий пункт глобального меню, то выполниться ленивая загрузка следующего модуля и так далее. Сокращение времени загрузки приложения Angular увеличивает количество обработанных запросов в единицу времени. Особенно это важно при масштабировании нашего сервера в облаке.
Если уже имеется большое монолитное приложение на Angular и стоит задача по его улучшению, то можно организовать его как приложение с одним загружаемым модулем. И начать выделять из него отдельные независимые модули. Модуль с отдельным функционалом будем называть доменным модулем. И эти доменные модули должны быть загружаемыми ленивой загрузкой (lm - loadable modules).
Создать каталог для проекта перейти в него:
$ mkdir /home/alexey/ws_ts3/crm-simple4/ && cd /home/alexey/ws_ts3/crm-simple4/
Установить локально требуемую версию @angular/cli (использовалась версия Angular 10):
$ npm install @angular/cli@10
Можно установить локально последнюю версию:
$ npm install @angular/cli@latest
В результате в текущем каталоге появляется новый подкаталог node_modules
, в котором содержится требуемая версия @angular/cli
.
Выполнить создание рабочего пространства и основного приложения crm-simple:
$ npx ng new crm-simple --directory=. --routing=true --style=scss
ng new crm-simple
- создать новое приложение--directory=.
- в текущем каталоге--routing=true
- генерировать модуль routing--style=scss
- использовать preprocessor 'scss'
Библиотека Angular Material содержит много полезных компонент, которые помогут нам создать надежное и красивое приложение. С описанием этой библиотеки можно ознакомится на сайте https://material.angular.io/.
Добавим в проект библиотеку Angular Material версии 10, так как был установлен Angular версии 10.
$ npx ng add @angular/material@10
Во время установки требуется указать тему оформления (например 'Indigo/Pink'). Так как все компоненты этой библиотеки поддерживают тему оформления (одну из предварительно установленных или пользовательскую). По этому требуется указать тему оформления по умолчанию.
И соглашаемся с установкой библиотеку анимации. Она обеспечит плавную анимацию работы стандартных компонент библиотеки Angular Material (таких как: кнопки, радио-кнопки и так далее). Без анимации не будет корректно работать компонент спинера.
На остальные параметры указываем ответ по умолчанию.
Так как данная библиотека имеет свой набор стилей, то этот набор автоматически добавляется в файл описания проекта.
./angular.json
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
Библиотека Angular Material потребуется, так как планируется использовать компонент MatTable для отображения таблицы.
Для работы приложения нам потребуется декоратор автоматической отписки от subscribe (AutoUnsubscribe). Добавим в приложение новый файл /src/app/_decorators/auto-unsubscribe.ts. Теперь можно указать данный декоратор для компонента и он автоматически выполнит все отписки subscribe.
Для продолжения переходим в каталог:
$ cd /home/alexey/ws_ts3/crm-simple4/
При загрузки приложения получаем профиль пользователя, в котором имеется массив с разрешениями. По данным массива разрешений определяем какие маршруты доступны текущему пользователю. Например, если массиве разрешений имеется значение lm-client, значит пользователю доступно глобальное меню Clients
и может загрузиться доменный модуль lm-client. Иначе, если в массиве разрешений нет значения lm-client, то пользователь не может загрузить доменный модуль lm-client и у него не будет элемента глобального меню Clients
.
Создадим интерфейс профиля пользователя.
$ npx ng generate interface _interfaces/profileDto interface
Создадим сервис для хранения профиля пользователя.
$ npx ng generate service _services/profile
Укажем данный сервис profile в списке провайдеров в основном модуле /app/app.module.ts. В других модулях нельзя указывать сервис profile в списке провайдеров, иначе будет создан новый экземпляр данного сервиса. Так как мы планируем получать данные профиля пользователя из доменных модулей, то экземпляр сервиса profile должен быть только один.
Добавим в основное приложение класс охранника. Данный класс решает следующие задачи:
- получить список требуемых разрешений из параметров маршрута Route.data.permissions;
- получить список имеющихся разрешений из профиля текущего пользователя;
- дать разрешение, если у пользователя имеются все требуемые разрешения;
Создадим охранника и укажем признаки создания двух интерфейсов:
- интерфейс CanActivate - активация маршрута;
- интерфейс CanLoad - загрузка маршрута с "ленивой загрузкой";
$ npx ng generate guard _guards/permissions --implements CanActivate --implements CanLoad
Для продолжения переходим в каталог:
$ cd /home/alexey/ws_ts3/crm-simple4/
Приставка lm (loadable modules) обозначает то, что это доменный модуль с ленивой загрузкой.
Добавим в наше приложение доменный модуль по работе с клиентами.
$ npx ng generate module lm-client --routing=true --route=lm-client --module=app-routing.module
-
--routing=true
- генерировать модуль routing. -
--route=lm-client
- наименование маршрута для модуля с отложенной загрузкой. Создает компонент в новом модуле и добавляет маршрут к этому компоненту вRoutes
, указанного в модуле опции--module
. -
--module=app-routing.module
- модуль в массивRoutes
которого добавляет маршрут к новому компоненту.
В результате в каталоге /app будет создан модуль и компонент lm-client. В родительском модуле app-routing.module модуль lm-client указан с ленивой загрузкой. В доменном модуле будут использоваться константы, создадим для них файл /_consts/lm-client.consts.ts.
Создадим компоненты для отображения списка клиентов. Каждый компонент имеет свой модуль, в котором описываются в списке импорта все требуемые для работы сторонние модули. Наличие у компонента модуля делает его самодостаточным и готовым к использованию.
У нас отсутствует BackEnd и для ответа сервера используем перехватчик, которые симулируют работу сервера API. Создадим файл, в котором будет логика работы с данными о клиентах.
Создание перехватчика по запросам работы с клиентами /app/_interceptors/mock-client.interceptor.ts.
$ npx ng generate interceptor _interceptors/mock-client
Добавим этот перехватчик в список провайдеров в главном модуле приложения /src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { environment } from '../environments/environment';
import { Tracing } from './_consts/app.consts';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MockClientInterceptor } from './_interceptors/mock-client.interceptor';
const provideMock = [
{ provide: HTTP_INTERCEPTORS, useClass: MockClientInterceptor, multi: true }
];
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule, // ** Must be loaded first **
BrowserAnimationsModule,
HttpClientModule,
AppRoutingModule
],
providers: [
...(!environment.production ? provideMock : [])
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor() {
Tracing.log('AppModule();');
}
}
Для продолжения переходим в каталог:
$ cd /home/alexey/ws_ts3/crm-simple4/src/app/lm-client/
Создадим интерфейс, в котором описаны свойства клиента.
$ npx ng generate interface _interfaces/clientDto interface
Создадим сервис для работы с данными о клиентах.
$ npx ng generate service /_services/client-api
Для работы данного сервиса требуется объект класса HttpClient
, который содержится в модуле HttpClientModule
. Так как сервисы будут создаваться и в других доменных модулях, то вынесем описание импорта модуля HttpClientModule
в главный модуль приложения /src/app/app.module.ts.
Создадим модуль и компонент для списка клиентов.
$ npx ng generate module client-list
$ npx ng generate component client-list --export=true
Создадим модуль и компонент c-l-header заголовка для списка клиентов.
$ npx ng generate module /client-list/c-l-header
$ npx ng generate component /client-list/c-l-header --export=true
Создадим модуль и компонент c-l-middle средней части для списка клиентов.
$ npx ng generate module /client-list/c-l-middle
$ npx ng generate component /client-list/c-l-middle --export=true
Создадим сервис Resolve для получения данных списка клиентов.
$ npx ng generate service /client-list/_resolvers/client-list-resolver
Создадим модуль и компонент для свойств клиента.
$ npx ng generate module client-view
$ npx ng generate component client-view --export=true
Создадим модуль и компонент c-v-header заголовка для свойств клиента.
$ npx ng generate module /client-view/c-v-header
$ npx ng generate component /client-view/c-v-header --export=true
Создадим модуль и компонент c-v-middle средней части для свойств клиента.
$ npx ng generate module /client-view/c-v-middle
$ npx ng generate component /client-view/c-v-middle --export=true
Создадим сервис Resolve для получения данных списка клиентов.
$ npx ng generate service /client-view/_resolvers/client-view-resolver
Создание маршрутов для модуля lm-client.
В доменном модуле lm-client для работы с клиентами опишем два маршрута:
-
/list
отображение списка клиентов; -
/view/:clientId
отображение свойств выбранного клиента;
Создадим файл /app/lm-client/lm-client-routing.module.ts с описанием этих маршрутов. Так же укажем для каждого маршрута раздел получения данных Resolve.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LmClientComponent } from './lm-client.component';
import { ClientListComponent } from './client-list/client-list.component';
import { ClientViewComponent } from './client-view/client-view.component';
import { ClientListResolverService } from './client-list/_resolvers/client-list-resolver.service';
import { ClientViewResolverService } from './client-view/_resolvers/client-view-resolver.service';
const itemRoutes: Routes = [
{
path: 'list',
component: ClientListComponent,
resolve: {
clientList: ClientListResolverService
}
},
{
path: 'view/:clientId',
component: ClientViewComponent,
resolve: {
client: ClientViewResolverService
}
},
{
path: '**',
redirectTo: 'list'
}
];
const routes: Routes = [
{ path: '', component: LmClientComponent, children: itemRoutes }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [
ClientListResolverService,
ClientViewResolverService
]
})
export class LmClientRoutingModule { }
Для продолжения переходим в каталог:
$ cd /home/alexey/ws_ts3/crm-simple4/
Добавим в наше приложение доменный модуль по работе с клиентами.
$ npx ng generate module lm-task --routing=true --route=lm-task --module=app-routing.module
-
--routing=true
- генерировать модуль routing. -
--route=lm-task
- наименование маршрута для модуля с отложенной загрузкой. Создает компонент в новом модуле и добавляет маршрут к этому компоненту вRoutes
, указанного в модуле опции--module
. -
--module=app-routing.module
- модуль в массивRoutes
которого добавляет маршрут к новому компоненту.
В результате в каталоге /app будет создан модуль и компонент lm-task. В родительском модуле app-routing.module модуль lm-task указан с ленивой загрузкой. Для констант создадим файл /_consts/lm-task.consts.ts.
У нас отсутствует BackEnd и для ответа сервера используем перехватчик, которые симулируют работу сервера API. Создадим файл, в котором будет логика работы с данными о задачах.
Создание перехватчика по запросам работы с задачами /app/_interceptors/mock-task.interceptor.ts.
$ npx ng generate interceptor _interceptors/mock-task
Добавим этот перехватчик в список провайдеров в главном модуле приложения /src/app/app.module.ts
const provideMock = [
{ provide: HTTP_INTERCEPTORS, useClass: MockClientInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: MockTaskInterceptor, multi: true }
];
Для продолжения переходим в каталог:
$ cd /home/alexey/ws_ts3/crm-simple4/src/app/lm-task/
Создадим интерфейс, в котором описаны свойства задач.
$ npx ng generate interface _interfaces/taskDto interface
Создадим сервис для работы с данными о задачах.
$ npx ng generate service /_services/task-api
Создадим модуль и компонент для списка задач.
$ npx ng generate module task-list
$ npx ng generate component task-list --export=true
Создадим модуль и компонент t-l-header заголовка для списка задач.
$ npx ng generate module /task-list/t-l-header
$ npx ng generate component /task-list/t-l-header --export=true
Создадим модуль и компонент t-l-middle средней части для списка задач.
$ npx ng generate module /task-list/t-l-middle
$ npx ng generate component /task-list/t-l-middle --export=true
Создадим сервис Resolve для получения данных списка задач.
$ npx ng generate service /task-list/_resolvers/task-list-resolver
Создадим модуль и компонент для свойств задачи.
$ npx ng generate module task-view
$ npx ng generate component task-view --export=true
Создадим модуль и компонент t-v-header заголовка для свойств задачи.
$ npx ng generate module /task-view/t-v-header
$ npx ng generate component /task-view/t-v-header --export=true
Создадим модуль и компонент t-v-middle средней части для свойств задачи.
$ npx ng generate module /task-view/t-v-middle
$ npx ng generate component /task-view/t-v-middle --export=true
Создадим сервис Resolve для получения данных списка задач.
$ npx ng generate service /task-view/_resolvers/task-view-resolver
Запустим и проверим работоспособность всего приложения командой:
$ npx ng serve --port 4250
И в браузере проверить по ссылке: http://localhost:4250/lm-client/list
Исходный код можно скачать github-crm-simple4. (Запустите npm install
перед запуском приложения.)
Запустить проект на сайте StackBlitz можно по ссылке https://stackblitz.com/github/alx-melnichuk/crm-simple4.