Хотя прошлая моя публикация была замечена рядом людей, тем не менее она получила клеймо «Перевода». Что без сомнений делает все мои рекомендации, отмеченные там, плагиатом. Ведь не существует едино��о мнения по ведению кода.

Поэтому, в этот раз, я решил рассказать вам свою историю о том, как я выполнял заказ по разработке странички для интернет магазина.

Возможно, вы скажите, что это довольно заезженная тема. Однако, не всё так просто. Если обычно сложности в работу накидывают сами заказчики, то тут я решил сам себе подыскать кочки. Дело в том, что я довольно давно уже отошёл от разработок простых сайтов, страниц и всяких CMS в пользу Single Page Application. Предложение доработать ряд элементов на странице, которая являлась частью уже готового сайта и имела ряд табу в подходе к разработке, не внушало мне особого интереса. Отмечу, что сама задача не сложная. Суть заключалась в том, чтобы выводить товары в соответствующем виде и со своей внутренней логикой. Так же, немаловажное условие было: лёгкое редактирование контента самим заказчиком. Так как сайт сам по себе разработан ни как SPA, то делать это средствами, мною любимого, Angular я не могу… или нет? Да, мне нельзя было подключать к сайту сам fraemwork, но я мог сделать bundle файл со всеми необходимыми мне условиями выполнения задачи.

Сразу скажу, что конечно существуют способы куда проще и правильнее, но в этом случае, я бы не стал браться за эту работу. Мною двигал интерес, в теории я знал, что это сработает. Я читал о неком CodeMix, компоненте, который поставляется с версии Angular 6 и выше. Знакомьтесь, Angular Elements. В двух словах они преобразуют компоненты в элементы HTML (+ JavaScript), позволяя нам использовать наши компоненты в других приложениях, разных fraemworks (тот же, React) или даже в простой странице HTML + JavaScript!

Я не буду описывать заказной вариант работы и в качестве примера приведу простой компонент панели, который будет отображать заголовок и содержимое под ним сообщение. Так же мы разместим, в рамках заголовка, checkbox для контроля за отображением содержимого сообщения. Затем мы продолжим использовать эту панель на простой HTML-странице. Тем самым решив задачу.

По рецепту нам понадобится:


  • Node.js,
  • Диспетчер пакетов узлов (npm),
  • Angular CLI (> 6.0.0),
  • CodeMix

В Eclipse создаём новый проект



File > Create > Project > CodeMix > Angular Project

Далее в терминале для проекта (Ctrl+Shift+P)



Вводим: Terminal: Create New Integrated Terminal

Терминал автоматически откроется в папке проекта. В нём мы вписываем npm install, ng serve. После, спокойно переходим на страницу: localhost:4200. Далее нам нужно задействовать модули Angular element и poly-fills для совместимости браузеров, поскольку они не поддерживаются как надо в браузерах от Microsoft.

npm install @angular/elements @webcomponents/custom-elements


Создадим компонент


Используя незамысловатые возможности Angular CLI, пишем следующее:

ng g c codemix-example

Наш компонент готов, осталось привести его в порядок.

HTML:

<div class="CodemixExampleComponentClass">
  <div class="head">
    <span>{{ title }}</span>
    <input type="checkbox" [checked]="isShow" (change)="changeShow()" />
  </div>
  <div class="body" *ngIf="isShow">{{ content }}</div>
</div>

TypeScript:

import { Component, Input, ViewEncapsulation } from '@angular/core';
@Component({
  selector: 'codemix-example',
  templateUrl: './codemix-example.component.html',
  styleUrls: ['./codemix-example.component.css'],
  encapsulation: ViewEncapsulation.Native
})
export class CodemixExampleComponent {
  public isShow: boolean;
  constructor() { }

  @Input() public content;
  @Input() public title;  

  public changeShow(): void {
    this.isShow = !this.isShow;
  }
}

В детали CSS я не буду углубляться, для этого примера я просто задал основным блокам свой цвет фона.

Проверяем наш компонент в том что он работает и продолжаем.

//app.module.ts.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { CodemixExampleComponent } from './codemix-example/codemix-example.component';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
    CodemixExampleComponent
  ],
  entryComponents: [CodemixExampleComponent],
  imports: [
    BrowserModule
  ],
  providers: []
})
export class AppModule {
  constructor(private injector: Injector){ }
  
  ngDoBootstrap(){
    const element = createCustomElement(
      CodemixExampleComponent, { injector: this.injector }
    );
    customElements.define('codemix-example', element)
  }
}

В приведенном выше коде мы остановили Angular от автоматической загрузки приложения. Мы достигли этого, удалив свойство bootstrap в объекте параметра decorator и перезаписав ngDoBootstrap метод в AppModule классе. Мы также добавляем компонент CodemixExampleComponent в массив entryComponents, чтобы инструктировать Angular создавать компонент, даже если он не является частью шаблона. Затем в ngDoBootstrap компонент CodemixExampleComponent анализируется с помощью функции createCustomElement из модуля Angular Elements. Теперь мы можем использовать элемент в HTML-файле в нашем проекте.

Например, index.html файл можно переписать следующим образом:

<!doctype html>
...
<body>
  <div style="width: 50vw; margin: auto;">
    <codemix-example 
      title="Title text" 
      content="Content text Content text  Content text Content text Content text Content text Content text Content text Content text Content text">
    </codemix-example>
  </div>
</body>
...

Получение bundle файла


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

Для этого нам понадобиться модуль concat:

npm install concat --save-dev

Остаётся создать скрипт сборки в корне папки проекта.

// build.js:
const concat = require('concat');
const files = [
   'node_modules/@webcomponents/custom-elements/src/native-shim.js',
    './dist/angulartest/runtime.js',
    './dist/angulartest/polyfills.js',
    './dist/angulartest/main.js'
];

const outputFile = './dist/bundle.js';
concat(files, outputFile);

Финальный штрих, запускаем команду, ну или добавляем её в package.json к объекту scripts:

ng build --prod --output-hashing = none && node build-script.js

В итоге мы получили заветный bundle.js. Файл, который теперь может быть использован в любом месте. Скопируйте этот файл в другое место и создайте простой HTML-файл в той же папке.

Если кому интересно на выходе у меня получилось следующее:



А вот и простейший index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Angulartest</title>
</head>
<body>
  <div style="width: 50vw; margin: auto;">
    <codemix-example 
      title="Title text" 
      content="Content text Content text  Content text Content text Content text Content text Content text Content text Content text Content text">
    </codemix-example>
  </div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>

Заключение


Как вы видите, мы просто использовали Angular компонент в простом HTML-файле. Да, здорово, что даже в таких задачах Angular способен о себе заявить. Мы способны использовать наш компонент, разработанный на Angular, в React, Vue и т.д., ну не круто ли? Но, как я и сказал в начале, это не единственный и точно не лучший вариант в той задаче, что решал я. Я сделал это только потому что мне было интересно сделать так. Важно знать, что такой способ не полностью поддерживается большинством браузеров. А так же вынуждает нас работать с файлом, который довольно большой для работы с всего лишь одним элементом. Например мой получился в 234КБ.