Как скомпилировать угловой элемент в веб-компонент с Webpack или угловым CLI?

Я создал простой Web-компонент через Angular, используя учебник Pascal Precht, который вы можете увидеть ЗДЕСЬ. Он автоматически волшебно компилируется в Stackblitz в ссылке, но не локально.

Моя конечная цель состоит в том, чтобы локально получить код для результирующего веб-компонента в отдельном файле. В конце концов, я буду загружать его куда-нибудь и вытягивать через один <script> тег, как и обычные веб-компоненты raw-html/javascript. Я думаю, что вопрос говорит сам за себя, но вы можете прочитать подробности ниже, если хотите:


Детали:

Чтобы подвести итог моего кода по ссылке выше, у меня есть очень простой компонент:

import { Component } from '@angular/core';

@Component({
  selector: 'hello-world',
  template: `<h1>Hello world</h1>`
})

export class HelloComponent  {}

и у меня есть модуль:

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

@NgModule({
  imports: [BrowserModule],
  declarations: [HelloComponent],
  entryComponents: [HelloComponent]
})

export class AppModule { 
  constructor(private injector: Injector) {}
  ngDoBootstrap() {
    const HelloElement = createCustomElement(HelloComponent, {
      injector: this.injector 
    });

    customElements.define('hello-world', HelloElement);
  }
}

Вот объяснение модуля выше:

  1. Добавить мой компонент в entryComponents массив, поэтому он не удаляется угловым шейкером (так как он недоступен при загрузке приложения: entryComponents: [HelloComponent]
  2. Запустите мой компонент через createCustomElement функция, так что я могу использовать его как обычный HTML Web Component:

    const HelloElement = createCustomElement (HelloComponent, {injector: this.injector});

  3. Наконец, я прошу Angular скомпилировать этот компонент в main.ts:

    platformBrowserDynamic().bootstrapModule(AppModule);


Вот материал, который я прочитал / посмотрел полностью (среди десятков других ссылок - большинство из которых датированы, как и оригинальное введение в Angular Elements):

Веб-компоненты с нуля Томека Сулковски (он никогда не компилирует его отдельно)
Веб-компоненты с CLI (та же проблема)
Веб-компоненты от Academind (опять же, этот парень также использует их в приложениях Angular)

Спасибо за любую помощь.

4 ответа

import { NgModule} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HelloComponent } from './hello.component';
import { AppComponent } from './app.component';

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent, HelloComponent],
  entryComponents: [HelloComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

убедитесь, что вы используете

npm install --save @angular/elementsи добавить "@webcomponents/custom-elements" : "^1.0.8" в package.json. После этого запуска npm install И наряду с этим вам нужно удалить комментарий ниже строк из polyfills.ts

Это добавляет polyfill, который требуется для пользовательских элементов для работы.

import '@webcomponents/custom-elements/custom-elements.min'; import '@webcomponents/custom-elements/src/native-shim';

<my-tag message="This is rendered dynamically">stack Overflow</my-tag>

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

В файле AppComponent.ts

 import { Component, Injector } from '@angular/core'; 
 import { createCustomElement } from '@angular/elements'
 import { DomSanitizer } from '@angular/platform-browser';

 import { HelloComponent } from './hello.component';

 @Component({
  selector: 'app-root',
  template: '<div [innerHtml]="title"></div>',
  styleUrls: ['./app.component.css']
 })
 export class AppComponent {
 title = null;

 constructor(injector: Injector, domsanitizer: DomSanitizer){
   const customElement = createCustomElement(HelloComponent, {injector: 
   injector});

   //this feature is not provided by angular it is provided by javascript
   //this allows us to register custom web component
   customElements.define('my-tag', customElement);
   //instead of 'hello-world' i've used 'my-tag'    
   setTimeout(() => {
    //security mechanism is used to avoid cross site attacks
    this.title = domsanitizer.bypassSecurityTrustHtml('<my-tag message="This 
    is rendered dynamically">stack Overflow</my-tag>');     
    }, 1000);
   }
 }

И внутри вашего HelloComponent

 import { Component, OnInit, Input } from '@angular/core';

 @Component({
 selector: 'hello-world',
 template: `<div> hello component -- {{ message }}</div>`,
 styles: [`
  div {
    border: 1px solid black;
    background-color: red;
    padding: 1%;
   }
 `]
})
export class HelloComponent implements OnInit {
@Input() message : string;

constructor() { }

ngOnInit() {
}
}

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

Я надеюсь, что это поможет вам запустить ваш код локально

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

Вот следующие шаги: 1. Добавьте ButtonComponent и AlertComponent в список entryComponent. В ngDoBootstrap и определите их как пользовательские элементы. Вот так выглядит мой app.module:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';

import { AppComponent } from './app.component';
import { ButtonComponent } from './button/button.component';
import { AlertComponent } from './alert/alert.component';

@NgModule({
  declarations: [AppComponent, ButtonComponent, AlertComponent],
  imports: [BrowserModule],
  entryComponents: [ButtonComponent, AlertComponent]
})
export class AppModule {
  constructor(private injector: Injector) {
  }

  ngDoBootstrap() {
    const customButton = createCustomElement(ButtonComponent, { injector: this.injector });
    customElements.define('my-button', customButton);

    const alertElement = createCustomElement(AlertComponent, { injector: this.injector});
    customElements.define('my-alert', alertElement);
  }
}
  1. Вот мой компонент кнопки:

    import {Input, Component, ViewEncapsulation, EventEmitter, Output} из '@ angular / core';

    @Component ({селектор: "настраиваемая кнопка", шаблон: <button (click)="handleClick()">{{label}}</button>, стили: [ button { border: solid 3px; padding: 8px 10px; background: #bada55; font-size: 20px; } ], encapsulation: ViewEncapsulation.Native}) класс экспорта ButtonComponent { @Input() label = 'метка по умолчанию'; @Output() action = new EventEmitter(); приватные клики Ct = 0;

    handleClick () {this.clicksCt ++; this.action.emit (this.clicksCt); }}

  2. Вот мой компонент оповещения:

    import {Component, Input, OnInit} из '@angular/core'; @Component({селектор: 'alert-message', шаблон: 'Alert Message: {{message}}', стили: [ ` div { border: 1px solid #885800; background-color: #ffcd3f; отступы: 10px; цвет: красный; поле: 10px; семейство шрифтов: Arial;

    }
    `]
    

    }) класс экспорта AlertComponent { @Input () message: string; }

  3. Построить конфигурации в angular.json:

    "build": {"builder": "@ angular-devkit / build-angular: browser", "options": {"outputPath": "dist", "index": "src / index.html", "main": "src / main.ts", "polyfills": "src / polyfills.ts", "tsConfig": "src / tsconfig.app.json", "assets": ["src / favicon.ico", "src / assets "]," styles ": [" src / styles.css "]," scripts ": [{" input ":" node_modules / document-register-element / build / document-register-element.js "}] }, "configurations": {"production": {"fileReplacements": [{"replace": "src / environment /environment.ts", "with": "src/environment /environment.prod.ts" } ], "оптимизация": true, "outputHashing": "all", "sourceMap": false, "extractCss": true, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true } } }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "angular6-elements:build" }, "configurations": { "production": { "browserTarget": "angular6-elements:build:production" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "angular6-elements:build" } }

  4. После сборки я объединяю runtime, polyfills, script JS файлы в один файл сценария и экспорт elements.js который содержит пользовательские элементы (необязательно: gzip эти файлы), обслуживает его с помощью http-сервера deploy --gzip

    "scripts": {"ng": "ng", "start": "ng serve", "build": "ng build --prod --output-hashing = none", "package": "npm run package package-base && npm run package-elements", "package-base": "cat dist/{runtime,polyfills,scripts}.js | gzip > deploy/script.js.gz", "package-elements": "cat dist/main.js | gzip > deploy/elements.js.gz", "serve": "http-server deploy --gzip", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }

  5. Наконец включаю script.js а также elements.js в index.html (в каталоге deploy), чтобы сообщить браузеру о пользовательских элементах. Теперь my-button и my-alert могут быть включены в index.html
. В этом примере кнопка отображается при загрузке, а оповещение добавляется динамически (с номером счетчика) при нажатии кнопки. Вот код:

    Пользовательская кнопка Тестовая страница

    const button = document.querySelector ('my-button'); const msgContainer = document.querySelector ('# message-container'); button.addEventListener ('action', (event) => {console.log ("action" emitted: ${event.detail}); button.setAttribute ("label", "Show Next Alert Message!"); msgContainer.innerHTML + = <my-alert message="Here is a message #${event.detail} created dynamically using ng elements!!!"></my-alert>; });

Вот моя ссылка на мой репозиторий

Надеюсь, это поможет!

Благодарю.

Привет.

Если я правильно понимаю, вы хотите создать веб-компонент (скажем, <my-component></my-component) и затем с помощью простого тега script, чтобы получить файл.js для инициализации этого компонента и добавления его на любую html-страницу, которую вы хотите.

В моем GitHub-репозитории я создал простой компонент Todo List. Этот компонент следует принципам угловых элементов, а также я установил несколько библиотек управления файлами для веб-пакета, чтобы также упаковать JS в один файл JS.

Вы можете проверить этот репозиторий и посмотреть, поможет ли это вам. Просто клонируйте его, а затем запустите npm install, а затем npm run build:elements Не стесняйтесь обращаться ко мне, если что-нибудь пойдет на юг.

Также проверьте это руководство. Этот парень мне очень помог.

Удачи

Из того, что я читал, упаковка, специфичная для компонентов Angular Elements для удобства использования вне Angular, будет идти с Angular 7.

Что вы можете сделать сейчас, это создать и угловое приложение с кли.

ng new YourAppName

Добавьте библиотеку угловых элементов:

ng add @angular/elements

Это также добавляет все необходимые полифилы, как описано в официальной угловой документации.

Затем вы изменяете AppModule, чтобы он не был модулем начальной загрузки, а просто регистрировал пользовательские элементы. Вы удаляете начальную загрузку из NgModule и рекламируете компоненты как входные компоненты. Затем зарегистрируйте компоненты в качестве пользовательских элементов в ngDoBootstrap крюк. Я сделал пользовательские элементы AppComponent и HelloComponent по умолчанию. Вот так выглядит мой модуль приложения:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';

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

@NgModule({
  declarations: [
    AppComponent,
    HelloComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  entryComponents: [AppComponent, HelloComponent]
})
export class AppModule {
  constructor(private injector: Injector) {
  }

  ngDoBootstrap() {
    customElements.define('app-root', createCustomElement(AppComponent, {injector: this.injector}));
    customElements.define('hello-world', createCustomElement(HelloComponent, {injector: this.injector}));
  }
 }

Затем вы можете использовать элементы в index.html как элементы, например, так:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>ElementsTest</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <div>
    <app-root></app-root>
  </div>
  <div>
    <hello-world></hello-world>
  </div>
</body>
</html>

Если вы построите это с ng build --prod вы получаете свернутые пакеты, которые теперь можно использовать и на других html-страницах, включив сценарии пакетов по мере их включения компилятором в файл index.html.

Я добавил свой образец в GitHub. В истории вы можете увидеть, что я изменил по сравнению с первоначальным приложением.

Другие вопросы по тегам