Как скомпилировать угловой элемент в веб-компоненте с помощью Webpack или Angular CLI?
Я создал простой веб-компонент через 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);
}
}
Ниже приведено объяснение модуля выше:
- Добавьте мой компонент в массив
entryComponents
чтобы он не был вынут угловым шейкере (поскольку он недоступен при загрузке приложения: entryComponents: [HelloComponent]
-
Запустите мой компонент через функцию createCustomElement
чтобы я мог использовать его как обычный Web Component
html:
const HelloElement = createCustomElement (HelloComponent, {injector: this.injector});
-
Наконец, я прошу Angular скомпилировать этот компонент в main.ts
:
platformBrowserDynamic().bootstrapModule(AppModule);
Вот материал, который я читал/смотрел полностью (среди десятков других ссылок, большинство из которых датированы, как и оригинальные вставки Angular Elements):
Веб-компоненты от Scratch от Tomek Sułkowski (он никогда не компилирует его отдельно)
Веб-компоненты с CLI (та же проблема)
Веб-компоненты от Academinary (опять же, этот парень также использует их в приложениях Angular)
Спасибо за любую помощь.
Ответы
Ответ 1
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
Это добавляет полиполк, который требуется для работы пользовательских элементов.
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>
Угловой не компилирует этот выше код, но угловые элементы устраняют эту проблему, позволяя взять наш угловой компонент и поместить его в полностью инкапсулированный самонастраиваемый 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() {
}
}
Теперь это загружается как родной веб-компонент. Используется только в угловых проектах, но уже используется для такого содержания.
Я надеюсь, что это поможет вам запустить свой код локально
Ответ 2
Из того, что я прочитал, упаковка, предназначенная для компонентов Angular Elements для легкого использования вне углового, будет поставляться с угловым 7.
Теперь вы можете создать и угловое приложение с кли.
ng new YourAppName
Добавьте библиотеку "Угловые элементы":
ng add @angular/elements
Это добавляет также все требуемые полисы, как описано в официальной Угловой документации.
Затем вы изменяете AppModule, чтобы не быть модулем начальной загрузки, а просто регистрировали пользовательские элементы. Вы удаляете загрузочный блок из NgModule и рекламируете компоненты как компоненты ввода. Затем зарегистрируйте компоненты как пользовательские элементы в ngDoBootstrap
hook. Я создал как пользовательские элементы 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. В истории вы можете увидеть, что я изменил из первоначального приложения cli.
Ответ 3
Текущая версия 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);
}
}
- Вот мой компонент кнопки:
import {
Input,
Component,
ViewEncapsulation,
EventEmitter,
Output
} from '@angular/core';
@Component({
selector: 'custom-button',
template: '<button (click)="handleClick()">{{label}}</button>',
styles: [
'
button {
border: solid 3px;
padding: 8px 10px;
background: #bada55;
font-size: 20px;
}
'
],
encapsulation: ViewEncapsulation.Native
})
export class ButtonComponent {
@Input() label = 'default label';
@Output() action = new EventEmitter<number>();
private clicksCt = 0;
handleClick() {
this.clicksCt++;
this.action.emit(this.clicksCt);
}
}
- Вот мой компонент оповещения:
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'alert-message',
template: '<div>Alert Message: {{message}}</div>',
styles: [
'
div {
border: 1px solid #885800;
background-color: #ffcd3f;
padding: 10px;
color: red;
margin:10px;
font-family: Arial;
}
']
})
export class AlertComponent {
@Input () message: string;
}
- Построить конфигурации в 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/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": 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"
}
}
- После сборки я объединяю
runtime, polyfills, script
файлы runtime, polyfills, script
js файлы runtime, polyfills, script
в один файл сценария и экспортирую elements.js
который содержит пользовательские элементы (необязательно: gzip эти файлы), которые обслуживают его с помощью http-сервера deploy --gzip
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod --output-hashing=none",
"package": "npm run 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"
}
- Наконец, я включаю
script.js
и elements.js
в index.html (в каталоге deploy), чтобы сообщить браузеру о пользовательских элементах. Теперь my-button и my-alert могут быть включены в index.html. В этом примере кнопка отображается при загрузке, а оповещение добавляется динамически (с номером счетчика) при нажатии кнопки. Вот код:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Button Test Page</title>
<script src="script.js"></script>
<script src="elements.js"></script>
</head>
<body>
<my-button label="Show Alert Message!"></my-button>
<p></p>
<div id="message-container"></div>
<script>
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>';
});
</script>
</body>
</html>
Вот моя ссылка на мой репозиторий
Надеюсь, это поможет!
Благодарю.
Ответ 4
Привет.
Если я правильно понимаю, вы хотите сгенерировать веб-компонент (например, <my-component></my-component
), а затем с помощью простого тега скрипта, чтобы получить файл.js для инициализации этого компонента и добавления его на любой html которую вы хотите.
В моем репозитории GitHub я создал простой компонент списка Todo. Этот компонент следует принципам угловых элементов, а также я установил некоторые библиотеки управления файлами для webpack, чтобы также упаковать JS в один JS файл.
Вы можете проверить этот репозиторий и посмотреть, поможет ли это вам. Просто клонируйте его, а затем запустите npm install, а затем выполните npm run build: elements Не стесняйтесь обращаться ко мне, если что-то идет на юг.
Также проверьте это руководство. Этот парень мне очень помог.
Удачи