AngularInDepth уходит от Medium. Эта статья, ее обновления и более свежие статьи размещены на новой платформе inDepth.dev

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

В этой серии статей я расскажу вам, как это вообще возможно - запускать приложения Angular в разных средах. Кроме того, мы узнаем, как создать собственную платформу Angular, которая отображает приложения внутри системного терминала с использованием графики ASCII.

Статьи:

Эта статья является последней в серии статей о платформах Angular. В этой статье я расскажу вам, как создать собственную платформу. Но прежде чем мы начнем, убедитесь, что вы понимаете, как работают платформы Angular, или просто просмотрите предыдущие статьи из этой серии.

Как я уже говорил в первой статье этой серии, Angular может выполняться в разных средах из-за абстракции. Довольно большая часть Angular объявлена ​​как абстрактные классы, и когда мы используем разные платформы, эти платформы предоставляют свои собственные реализации для этих абстрактных классов. Вот почему для создания терминальной платформы нам просто нужно предоставить ряд реализаций сервисов для Angular.

Оглавление

  • Рендерер
  • Дезинфицирующее средство
  • Обработка ошибок
  • Терминальный модуль
  • Платформенный терминал
  • Приложение для создания терминала

Рендерер

Начнем с самой важной части терминала платформы - рендерера. Angular использует абстракцию Renderer для выполнения рендеринга приложения вне зависимости от среды. Средство визуализации - это просто абстрактный класс, поэтому мы собираемся создать реализацию, которая будет отображать приложения внутри системного терминала с использованием графики ASCII.

Но подождите, как мы будем отображать приложения внутри системного терминала? Я думаю, что самый простой способ - найти подходящую библиотеку, которая может создавать некоторые виджеты в терминале с использованием графики ASCII. Я решил использовать blessed - подобную curses библиотеку с высокоуровневым API интерфейса терминала для node.js. Прежде всего, давайте установим библиотеку с соответствующими типами:

npm install blessed @types/blessed

Поскольку я решил использовать специальную библиотеку для рендеринга пользовательского интерфейса, единственное, что мне нужно здесь сделать, - это создать какой-то адаптер. Нам нужно найти способ сопоставить API рендеринга Angular с API благословенной библиотеки.

Что ж, вот упрощенное объявление Angular Renderer:

В его обязанности входит создание и удаление элементов, добавление классов и атрибутов, регистрация слушателей событий и т. Д.

С другой стороны, у нас есть благословенная библиотека со следующим интерфейсом:

Как вы могли заметить здесь, blessed - это простая библиотека node.js, которая предоставляет здесь screen и ряд компонентов. Screen - это разновидность браузера document. Он служит корневым элементом приложения, а также содержит ряд полезных API.

Экран

Приступим к интеграции с screen с помощью рендерера. Прежде всего, давайте создадим отдельную Screen службу, которая будет предоставлять screen для модуля рендеринга.

Здесь у нас есть базовая реализация Screen. Служба отвечает за создание благословенного экрана, как было сказано выше, а также за настройку прослушивателя выхода. Потому что мы собираемся выполнять наши приложения внутри терминала, и это довольно распространенное поведение, когда терминальные приложения закрываются при нажатии control+c. Вот почему нам нужно отслеживать это событие на экране и выходить из процесса в качестве реакции. process.exit - это стандартный API-интерфейс node.js, который позволяет сценарию завершать работу с соответствующим кодом. 0 означает, что процесс завершился без ошибок. Кроме того, здесь мы могли видеть, что Screen предоставляет возможность выбрать корневой элемент для нашего приложения с помощью selectRootElement вызова.

Реестр элементов

Когда у нас есть Screen и мы можем выбрать корневой элемент, пора заняться процессом создания элементов. Как я сказал выше, blessed предоставляет возможность создавать элементы на экране с помощью набора функций, экспортируемых напрямую через пакет blessed. Между тем, рендерер Angular использует одну createElement функцию. Вот почему нам нужно создать какой-то адаптер для логики создания этого элемента. Например, я решил обернуть логику создания blessed элементов в специальный сервис - ElementsRegistry. ElementsRegistry отвечает за создание blessed элементов с помощью единственной createElement функции:

Итак, как вы можете заметить здесь, ElementsRegistry имеет единственный метод createElement, который фактически пытается найти запрошенный элемент на карте элементов и вернуть экземпляр этого элемента. Если элемент не найден, ElementsRegistry вернется к элементу box, который является аналогом элемента div в браузере.

На этом этапе у нас есть все необходимые сущности для создания Angular Renderer, который будет отображать приложения непосредственно внутри системного терминала с использованием графики ASCII.

Renderer

Вот базовая реализация рендерера.

Я решил реализовать здесь только небольшую часть всех необходимых методов, а остальные реализации оставил вам. Итак, у нас есть класс TerminalRenderer, реализующий интерфейс Renderer2. Он использует созданные выше Screen и ElementsRegistry для создания элементов.

Но послушайте, TerminalRenderer здесь не служба Injectable. Angular требует, чтобы Renderer был создан через RendererFactory. Итак, добавим еще одно:

TerminalRendererFactory здесь реализует RendererFactory2 интерфейс и реализует только один метод - createRenderer, который фактически создает новый экземпляр TerminalRenderer с необходимыми службами.

На этом этапе у нас есть полнофункциональный Angular TerminalRenderer, который может отображать приложения Angular внутри системного терминала с использованием графики ASCII. Но нам еще нужно добавить много, так что давайте копнем глубже 🔥

Дезинфицирующее средство

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

Например, при привязке URL-адреса в <a [href]=”someValue”> гиперссылке someValue будет очищен, чтобы злоумышленник не мог ввести, например, a javascript: URL-адрес, который будет выполнять код на веб-сайте. В определенных ситуациях может потребоваться отключить очистку, например, если приложению
действительно необходимо создать ссылку в стиле javascript: с динамическое значение в нем. Пользователи могут обойти безопасность, создав значение с помощью одного из bypassSecurityTrust... методов, а затем привязываясь к этому значению из шаблона.

Но внутри терминала у нас нет DOM, поэтому атаки с использованием межсайтовых сценариев не могут выполняться. Но если атаки с использованием межсайтовых сценариев не могут быть выполнены, нам не нужно очищать какие-либо значения шаблонов. Вот почему мы можем предоставить Angular просто пустую реализацию Sanitizer:

Как вы можете заметить, эта реализация TerminalSanitizer просто ничего не делает, она просто возвращает принятое значение.

Обработка ошибок

Каждое хорошее приложение должно уметь правильно работать с ошибками. Приложения Angular не исключение. Вот почему Angular предоставляет возможность настроить глобальный ErrorHandler, который будет реагировать на каждое необработанное исключение в ваших приложениях. Angular предоставляет реализацию по умолчанию для ErrorHandler, которая использует браузер console для правильного протоколирования всех необработанных исключений. Но для терминала этого мало.

В терминале у нас есть дополнительная проблема - если приложение где-то выдает исключение, ErrorHandler просто регистрирует его. Но приложение останется зависшим. В браузере мы можем просто перезагрузить вкладку с приложением, но это невозможно сделать прямо в терминале. Вот почему нам нужно предоставить специальную реализацию для ErrorHandler, которая не только регистрирует исключение, но и завершает текущий процесс:

Вот базовая реализация ErrorHandler. Он просто регистрирует ошибку в консоли, а затем выходит из процесса с кодом ошибки 1, что означает, что во время выполнения приложения что-то пошло не так.

Терминальный модуль

Как вы помните, каждое приложение Angular, созданное с помощью интерфейса командной строки Angular, настроено на выполнение в браузере по умолчанию, поэтому оно содержит BrowserModule, импортированный в AppModule. BrowserModule содержит ряд поставщиков для конкретных браузеров, а также реэкспортов CommonModule и ApplicationModule. Эти модули содержат несколько важных поставщиков для приложений Angular. Платформа терминала также требует этих провайдеров, поэтому нам нужно создать пользовательский TerminalModule, который будет реэкспортировать CommonModule и ApplicationModule, также как часть регистрации созданных выше сервисов в приложении.

Но не все службы можно зарегистрировать через TerminalModule, некоторые из них требуются на этапе начальной загрузки и должны быть предоставлены раньше. В этом случае единственный способ предоставлять услуги - это создать собственную платформу.

Платформенный терминал

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

На этом этапе мы сделали все, и пришло время создать приложение Angular для терминала.

Приложение для создания терминала

Прежде всего, давайте создадим новое приложение Angular с помощью Angular CLI:

ng new AngularTerminalApp

Затем добавим TerminalModule в раздел AppModule импорта:

Когда AppModule будет готов, пора настроить платформу терминала:

Здесь вы могли заметить, как терминальная платформа импортирована из пакета platform-terminal, который мы создали ранее. А затем используется для начальной загрузки AppModule нашего приложения.

Единственное, что нам здесь нужно сделать, это создать createAppComponent со всеми необходимыми элементами для приложения:

AppComponent здесь довольно тупой, так что оставлю без комментариев.

Затем нам нужно как-то скомпилировать приложение. Это можно сделать с помощью Angular Compiler CLI:

ngc -p tsconfig.json 

Angular Compiler CLI создаст скомпилированные файлы приложения в папке dist прямо в корне папки проекта. Итак, нам просто нужно загрузить его как простое приложение node.js:

node ./dist/main.js

И тогда мы увидим:

Заключение

Поздравляем вы дошли до конца статьи. В этой статье мы много узнали об Angular платформах. Прошел процесс создания пользовательской платформы. Узнал о важнейших сервисах и модулях Angular и, наконец, создал специальную платформу, которая отображает приложения Angular внутри системного терминала с использованием графики ASCII!

Вот репозиторий со всеми исходными файлами, относящимися к платформе Терминала: https://github.com/Tibing/platform-terminal

Если вы хотите получить более глубокие знания о платформах Angular, ознакомьтесь с остальными статьями этой серии:

Также подписывайтесь на меня в twitter, чтобы получать уведомления о новых статьях об Angular как можно скорее!