Часть 1, Часть 2, Часть 3, Часть 4
В этой статье, последней из серии статей «Платформы Angular в деталях», мы с вами завершим процесс создания пользовательской платформы. Но прежде чем начать, пожалуйста, просмотрите ещё раз предыдущие статьи, чтобы убедиться, что вы понимаете, как работают платформы Angular.
Содержание
- Очистка
- Обработка ошибок
- Модуль терминала
- Терминал платформы
- Создание приложения в терминале
Напоминаю, что в прошлой статье мы создали полнофункциональный TerminalRenderer, способный выводить Angular-приложения на экран из системного терминала с помощью графики ASCII. Но нам ещё много чего надо добавить. Так что идём дальше 🔥.
Очистка
Angular использует абстракцию Sanitizer для очистки потенциально опасных значений. Она требуется Angular для загрузки приложения.
Sanitizer описывается в Angular как абстрактный класс, поэтому браузерная платформа предоставляет собственную реализацию — DomSanitizer. DomSanitizer помогает предотвратить атаки межсайтового скриптинга, очищая значения для безопасного использования в DOM.
Например, при привязке URL-адреса в гиперссылке <a [href]=”someValue”> значение someValue будет очищено, чтобы злоумышленник не мог внедрить, скажем, javascript: URL-адрес, выполняющий код на сайте. В конкретных ситуациях может быть необходимо отключить очистку — если приложению, допустим, понадобится javascript, чтобы вставить в ссылку динамическое значение. Пользователи могут обойти защиту, создав значение с помощью одного из методов bypassSecurityTrust… и затем привязав к нему значение из шаблона.
Однако внутри терминала у нас нет DOM, поэтому атаки межсайтового скриптинга здесь невозможны. А раз так, то нет необходимости очищать шаблонные значения. Вот почему мы можем использовать в Angular пустую реализацию Sanitizer:
import { Sanitizer, SecurityContext } from '@angular/core'; export class TerminalSanitizer extends Sanitizer { sanitize(context: SecurityContext, value: string): string { return value; } }
Как видно, реализация TerminalSanitizer только возвращает принятое значение.
Обработка ошибок
Любое хорошее приложение должно уметь правильно справляться с ошибками, и Angular-приложения не являются исключением. Поэтому здесь есть возможность установить глобальный обработчик ошибок ErrorHandler, который будет реагировать на каждую нештатную ситуацию или ошибку в ваших приложениях. В Angular предусмотрена реализация по умолчанию для ErrorHandler. Она использует браузерную console, чтобы правильно регистрировать все нештатные ситуации. Но для терминала этого недостаточно.
В терминале появляется дополнительная проблема. Если приложение выбрасывает где-то ошибку, ErrorHandler просто регистрирует её, но приложение не отвисает. В браузере мы бы просто перезагрузили вкладку с приложением, но в терминале этого сделать не получится. Поэтому нам нужна пользовательская реализация для ErrorHandler, которая не только регистрирует ошибку, но и выходит из текущего процесса:
import { ErrorHandler, Injectable } from '@angular/core'; @Injectable() export class TerminalErrorHandler implements ErrorHandler { handleError(error: Error): void { console.error(error.message, error.stack); process.exit(1); } }
Вот базовая реализация для ErrorHandler. Она только регистрирует ошибку в консоли, а затем выходит из процесса с кодом ошибки 1. Этот код сигнализирует, что что-то пошло не так во время выполнения приложения.
Модуль терминала
Любое приложение, созданное с помощью Angular CLI, по умолчанию настроено на выполнение в браузере. Поэтому в нём содержится BrowserModule, импортированный в AppModule. В BrowserModule есть несколько связанных с браузером провайдеров. Кроме того, он повторно экспортирует CommonModule и ApplicationModule. Эти модули содержат множество провайдеров, критически важных для Angular-приложений.
В этих провайдерах нуждается и терминал платформы, поэтому нам надо создать пользовательский TerminalModule. Он повторно экспортирует CommonModule и ApplicationModule, а также регистрирует часть созданных выше сервисов в приложении.
import { CommonModule, ApplicationModule, ErrorHandler, NgModule, RendererFactory2 } from '@angular/core'; import { Screen } from './screen'; import { ElementsRegistry } from './elements-registry'; import { TerminalRendererFactory } from './renderer'; import { TerminalErrorHandler } from './error-handler'; @NgModule({ exports: [CommonModule, ApplicationModule], providers: [ Screen, ElementsRegistry, { provide: RendererFactory2, useClass: TerminalRendererFactory }, { provide: ErrorHandler, useClass: TerminalErrorHandler }, ], }) export class TerminalModule { }
Однако не все сервисы могут регистрироваться через TerminalModule. Некоторые из них требуются во время загрузки и должны быть подготовлены заранее. Единственный способ это сделать – создать пользовательскую платформу.
Терминал платформы
import { COMPILER_OPTIONS, createPlatformFactory, Sanitizer } from '@angular/core'; import { ɵplatformCoreDynamic as platformCoreDynamic } from '@angular/platform-browser-dynamic'; import { DOCUMENT } from '@angular/common'; import { ElementSchemaRegistry } from '@angular/compiler'; import { TerminalSanitizer } from './sanitizer'; const COMMON_PROVIDERS = [ { provide: DOCUMENT, useValue: {} }, { provide: Sanitizer, useClass: TerminalSanitizer, deps: [] }, ]; export const platformTerminalDynamic = createPlatformFactory(platformCoreDynamic, 'terminalDynamic', COMMON_RPOVIDERS]);
Терминал платформы создаётся через функцию createPlatformFactory, позволяющую наследовать провайдеры platformCoreDynamic, а также добавлять соответствующие провайдеры терминала платформы.
Теперь у нас все готово. Настала пора создать Angular-приложение в терминале.
Создание приложения в терминале
Прежде всего создадим новое Angular-приложение с помощью Angular CLI:
ng new AngularTerminalApp
Затем добавим TerminalModule в раздел импортирования AppModule:
import { NgModule } from '@angular/core'; import { TerminalModule } from 'platform-terminal'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent, ], imports: [ TerminalModule, ], bootstrap: [AppComponent], }) export class AppModule { }
Когда с AppModule закончили, пора настроить терминал платформы:
import { platformTerminalDynamic } from 'platform-terminal'; import { enableProdMode } from '@angular/core'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } platformTerminalDynamic().bootstrapModule(AppModule) .catch(err => console.error(err));
Здесь показано, как терминал платформы импортируется из созданного нами ранее пакета platform-terminal и используется для загрузки AppModule нашего приложения.
Единственное, что нам здесь надо сделать, — это создать для приложения AppComponent со всеми требующимися элементами:
import { ChangeDetectionStrategy, Component } from '@angular/core'; import { TransactionsService } from '../transactions.service'; import { SparklineService } from '../sparkline.service'; import { ServerUtilizationService } from '../server-utilization.service'; import { ProcessManagerService } from '../process-manager.service'; @Component({ selector: 'app-component', template: ` <grid rows="12" cols="12"> <line [row]="0" [col]="0" [rowSpan]="3" [colSpan]="3" label="Total Transactions" [data]="transactions$ | async"> </line> <bar [row]="0" [col]="3" [rowSpan]="3" [colSpan]="3" label="Server Utilization (%)" [barWidth]="4" [barSpacing]="6" [xOffset]="3" [maxHeight]="9" [data]="serversUtilization$ | async"> </bar> <line [row]="0" [col]="6" [rowSpan]="6" [colSpan]="6" label="Total Transactions" [data]="transactions$ | async"> </line> <table [row]="3" [col]="0" [rowSpan]="3" [colSpan]="6" fg="green" label="Active Processes" [keys]="true" [columnSpacing]="1" [columnWidth]="[28,20,20]" [data]="process$ |async"> </table> <map [row]="6" [col]="0" [rowSpan]="6" [colSpan]="9" label="Servers Location"> </map> <sparkline row="6" col="9" rowSpan="6" colSpan="3" label="Throughput (bits/sec)" [tags]="true" [style]="{ fg: 'blue', titleFg: 'white', border: {} }" [data]="sparkline$ | async"> </sparkline> </grid> `, changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppComponent { transactions$ = this.transactionsService.transactions$; sparkline$ = this.sparklineService.sparkline$; serversUtilization$ = this.serversUtilization.serversUtilization$; process$ = this.processManager.process$; constructor(private transactionsService: TransactionsService, private sparklineService: SparklineService, private serversUtilization: ServerUtilizationService, private processManager: ProcessManagerService) { } }
Теперь надо как-то скомпилировать приложение. Сделаем это с помощью компилятора Angular Compiler CLI:
ngc -p tsconfig.json
Компилятор Angular Compiler CLI сформирует файлы скомпилированного приложения прямо в корневом каталоге проекта. Так что нам лишь надо загрузить его как обычное приложение node.js:
node ./dist/main.js
И затем мы увидим:
Заключение
Поздравляю! Вы добрались до конца статьи, в которой мы многое узнали об Angular-платформах, познакомились с важными сервисами и модулями Angular и, наконец, создали ту самую пользовательскую платформу, которая визуализирует Angular-приложения в терминале с помощью графики ASCII!
В репозитории вы можете найти все исходные файлы, связанные с терминалом платформы https://github.com/Tibing/platform-terminal
Специально для сайта ITWORLD.UZ. Новость взята с сайта NOP::Nuances of programming