[Разработка веб-сайтов, Программирование] Custom Elements из Angular в Angular
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Angular позволяет создавать кастомные веб-компоненты на основе своих компонентов. Зачем?Код, собранный в веб-компонент, можно использовать где угодно, достаточно только подключить сгенерированный скрипт. Компонент можно внедрить в код страницы, причём, всё равно каким образом: можно в HTML (<my-custom-component></my-custom-component>), можно через DOM-API (document.body.append(document.createElement('my-custom-component')) ), после этого получим работающее микро-приложение c Angular под капотом.ЗадачаПередо мной стояла задача использовать интегрировать сгенерированные компоненты из одного Angular проекта в другое Angular приложение, максимально сохранив доступную функциональность. Почему нельзя было просто скопировать код компонентов и собрать второе приложение с ними? Потому что предполагается, что компоненты будут создаваться программистами-пользователями и в момент сборки неизвестно, сколько и какими они будут.Итого, задача: динамически создать и отобразить зарегистрированные в браузере веб-компоненты.Странно, но готовое решение нагуглить не получилось. Пришлось городить велосипед. Пост пишется в том числе и с целью получить правильное решение в комментариях.Процесс решенияВнимание! Весь код в статье не тестировался и даже не собирался. Приводится исключительно для примера.Angular позволяет динамически создавать компоненты и в документации описано, как это делать. Проблема в том, что для этого на этапе сборки необходимо знать тип компонента. Не подходит.NB: По-моему, раньше можно было создавать элементы по селектору, но задепрекейтили.Первая попыткаХорошо, там сверху написано, что достаточно просто сгенерировать правильный HTML, попробуем сделать это влоб, через динамически создаваемый компонент-обёртку (и не забыть про санитайзинг):
this.componentTagName = '<my-custom-component></my-custom-component>';
<div [innerHtml]="componentTagName"></div>
Странно, работает.А как насчёт @Input/@Output в этом HTML?
this.componentTagName = '<my-custom-component [someInput]="someInputVariable"></my-custom-component>';
Не работает. Что там с dependency injection, я думаю, тоже понятно.Вторая попыткаТак, у нас есть Renderer2, попробуем через него.
<div #componentContainerRef>
Component '{{componentTagName}}' is not registered in the browser's component registry yet.
</div>
и
customElements.whenDefined(this.componentTagName).then(() => {
const element = this.renderer.createElement(this.componentTagName);
this.renderer.appendChild(this.componentContainerRef.nativeElement, element);
});
Работает. Снова, как насчёт @Input? Естественно, если отображаемый веб-компонент его поддерживает:
customElements.whenDefined(this.componentTagName).then(() => {
const element = this.renderer.createElement(this.componentTagName);
this.renderer.setProperty(element, 'someInput', this.componentInput);
this.renderer.appendChild(this.componentContainerRef.nativeElement, element);
});
Работает. А @Output? Только не через setProperty (я его ещё не тестировал, потому что, теоретически, компоненты могут быть абсолютно любыми, не только Angular'овскими), а просто поймаем какое-нибудь событие (типа CustomEvent), которое отправил веб-компонент:
customElements.whenDefined(this.componentTagName).then(() => {
const element = this.renderer.createElement(this.componentTagName);
this.renderer.setProperty(element, 'someInput', this.componentInput);
this.renderer.listen(
element,
'customComponentEvent',
event => this.onEventReceivedFromComponent
);
this.renderer.appendChild(this.componentContainerRef.nativeElement, element);
});
onEventReceivedFromComponent(event: CustomEvent) {
console.info(`${this.componentTagName} received event: `, event);
}
Хм, не работает, теряется this, тогда попробуем вот так:
this.renderer.listen(
element,
'customComponentEvent',
event => this.onEventReceivedFromComponent.apply(this, [event])
);
Работает.А что с dependency injection? Можно инжектить всё что хочешь в компонент-обёртку и реализовать логику в нём. Как передать/получить данные в/из веб компонента мы уже знаем. Например, вот код компонента и его обёртки с подключением к общей шине событий:
// Angular Elements component
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
const tagName = 'my-custom-component';
@Component({
selector: tagName,
templateUrl: '<input [(ngModel)]="messageText" type="text"><button (click)="onSendMessageClick()">Send message</button>'
})
export class ExternalWidgetComponent implements OnInit {
@Input('messageReceiver') messageReceiver: EventEmitter<any>;
@Output('customComponentMessage') messageSender = new EventEmitter<any>();
static readonly tagName = tagName;
messageText: string = '';
constructor() {
}
ngOnInit(): void {
if (this.messageReceiver) {
this.messageReceiver.subscribe(this.onMessageReceived);
}
}
onSendMessageClick(): void {
this.messageSender.emit({text: this.messageText});
}
onMessageReceived(message) {
console.log(`${tagName} received the message: `, message);
}
}
// Wrapper component
import { Component, ElementRef, EventEmitter, Input, Renderer2, ViewChild } from '@angular/core';
import { EventBusService } from '../event-bus.service';
@Component({
selector: 'web-component-wrapper',
template: `<div #componentContainerRef>Component '{{componentTagName}}' is not registered in the browser's component registry yet.</div>`
})
export class WebComponentWrapperComponent {
@ViewChild('componentContainerRef') componentContainerRef: ElementRef;
componentTagName = '';
messageSender: EventEmitter<any> = new EventEmitter<any>();
// message name for the event bus
private messageType = 'customComponentMessage';
constructor(private renderer: Renderer2, private eventBus: EventBusService) {
}
renderComponentWhenDefined() {
customElements.whenDefined(this.componentTagName).then(() => {
const element = this.renderer.createElement(this.componentTagName);
this.renderer.setProperty(element, 'messageReceiver', this.messageSender);
this.renderer.listen(
element,
'customComponentEvent',
event => this.onEventReceivedFromComponent.apply(this, [event])
);
this.renderer.appendChild(this.componentContainerRef.nativeElement, element);
});
// re-route messages from the event bus to the web-component
this.eventBus
.observe(this.messageType)
.subscribe(this.sendMessageToComponent(message));
}
set componentName(value: string) {
this.componentTagName = value;
this.renderComponentWhenDefined();
}
// re-route messages from the component to the event bus
onMessageReceivedFromComponent(message: CustomEvent) {
this.eventBus.publish(this.messageType, message.detail);
}
sendMessageToComponent(message: any) {
this.messageSender.emit(message);
}
}
ИтогТаким образом можно интегрировать в Angular-приложение доступные в браузере веб-компоненты, при условии, что известно имя компонента и сигнатуры его @Input/@Output (если их нет, см. первую попытку).Спасибо за внимание.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Assembler, Научно-популярное, Старое железо, DIY или Сделай сам] Пишем программу для компьютера ALTAIR 8800 1975г выпуска
- [Firefox, Разработка веб-сайтов, Open source, Google Chrome, Браузеры] Невменяемый, необъятный масштаб браузеров (перевод)
- [Программирование, Rust] Размышления о Rust
- [Программирование] Mutation Driven Development
- [Python, Программирование, Django] Конвертеры маршрутов в Django 2.0+ (path converters)
- [Настройка Linux, Разработка веб-сайтов, CSS, JavaScript] Просто вертикальный монитор не значит, что я на телефоне (перевод)
- [Программирование, Go] Создаем бессерверное приложение с помощью Azure Functions и Go (перевод)
- [Системное администрирование, Программирование, Кодобред, *nix, Оболочки] Консольный менеджер сертификатов для JKS/PCKS12
- [Open source, Программирование, Совершенный код, C++] Исследование COVID-19 и неинициализированная переменная
- [Open source, Программирование, Совершенный код, C++] COVID-19 Research and Uninitialized Variable
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_programmirovanie (Программирование), #_angular, #_webcomponents, #_angular_elements, #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_programmirovanie (
Программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 14:05
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Angular позволяет создавать кастомные веб-компоненты на основе своих компонентов. Зачем?Код, собранный в веб-компонент, можно использовать где угодно, достаточно только подключить сгенерированный скрипт. Компонент можно внедрить в код страницы, причём, всё равно каким образом: можно в HTML (<my-custom-component></my-custom-component>), можно через DOM-API (document.body.append(document.createElement('my-custom-component')) ), после этого получим работающее микро-приложение c Angular под капотом.ЗадачаПередо мной стояла задача использовать интегрировать сгенерированные компоненты из одного Angular проекта в другое Angular приложение, максимально сохранив доступную функциональность. Почему нельзя было просто скопировать код компонентов и собрать второе приложение с ними? Потому что предполагается, что компоненты будут создаваться программистами-пользователями и в момент сборки неизвестно, сколько и какими они будут.Итого, задача: динамически создать и отобразить зарегистрированные в браузере веб-компоненты.Странно, но готовое решение нагуглить не получилось. Пришлось городить велосипед. Пост пишется в том числе и с целью получить правильное решение в комментариях.Процесс решенияВнимание! Весь код в статье не тестировался и даже не собирался. Приводится исключительно для примера.Angular позволяет динамически создавать компоненты и в документации описано, как это делать. Проблема в том, что для этого на этапе сборки необходимо знать тип компонента. Не подходит.NB: По-моему, раньше можно было создавать элементы по селектору, но задепрекейтили.Первая попыткаХорошо, там сверху написано, что достаточно просто сгенерировать правильный HTML, попробуем сделать это влоб, через динамически создаваемый компонент-обёртку (и не забыть про санитайзинг): this.componentTagName = '<my-custom-component></my-custom-component>';
<div [innerHtml]="componentTagName"></div>
this.componentTagName = '<my-custom-component [someInput]="someInputVariable"></my-custom-component>';
<div #componentContainerRef>
Component '{{componentTagName}}' is not registered in the browser's component registry yet. </div> customElements.whenDefined(this.componentTagName).then(() => {
const element = this.renderer.createElement(this.componentTagName); this.renderer.appendChild(this.componentContainerRef.nativeElement, element); }); customElements.whenDefined(this.componentTagName).then(() => {
const element = this.renderer.createElement(this.componentTagName); this.renderer.setProperty(element, 'someInput', this.componentInput); this.renderer.appendChild(this.componentContainerRef.nativeElement, element); }); customElements.whenDefined(this.componentTagName).then(() => {
const element = this.renderer.createElement(this.componentTagName); this.renderer.setProperty(element, 'someInput', this.componentInput); this.renderer.listen( element, 'customComponentEvent', event => this.onEventReceivedFromComponent ); this.renderer.appendChild(this.componentContainerRef.nativeElement, element); }); onEventReceivedFromComponent(event: CustomEvent) { console.info(`${this.componentTagName} received event: `, event); } this.renderer.listen(
element, 'customComponentEvent', event => this.onEventReceivedFromComponent.apply(this, [event]) ); // Angular Elements component
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; const tagName = 'my-custom-component'; @Component({ selector: tagName, templateUrl: '<input [(ngModel)]="messageText" type="text"><button (click)="onSendMessageClick()">Send message</button>' }) export class ExternalWidgetComponent implements OnInit { @Input('messageReceiver') messageReceiver: EventEmitter<any>; @Output('customComponentMessage') messageSender = new EventEmitter<any>(); static readonly tagName = tagName; messageText: string = ''; constructor() { } ngOnInit(): void { if (this.messageReceiver) { this.messageReceiver.subscribe(this.onMessageReceived); } } onSendMessageClick(): void { this.messageSender.emit({text: this.messageText}); } onMessageReceived(message) { console.log(`${tagName} received the message: `, message); } } // Wrapper component import { Component, ElementRef, EventEmitter, Input, Renderer2, ViewChild } from '@angular/core'; import { EventBusService } from '../event-bus.service'; @Component({ selector: 'web-component-wrapper', template: `<div #componentContainerRef>Component '{{componentTagName}}' is not registered in the browser's component registry yet.</div>` }) export class WebComponentWrapperComponent { @ViewChild('componentContainerRef') componentContainerRef: ElementRef; componentTagName = ''; messageSender: EventEmitter<any> = new EventEmitter<any>(); // message name for the event bus private messageType = 'customComponentMessage'; constructor(private renderer: Renderer2, private eventBus: EventBusService) { } renderComponentWhenDefined() { customElements.whenDefined(this.componentTagName).then(() => { const element = this.renderer.createElement(this.componentTagName); this.renderer.setProperty(element, 'messageReceiver', this.messageSender); this.renderer.listen( element, 'customComponentEvent', event => this.onEventReceivedFromComponent.apply(this, [event]) ); this.renderer.appendChild(this.componentContainerRef.nativeElement, element); }); // re-route messages from the event bus to the web-component this.eventBus .observe(this.messageType) .subscribe(this.sendMessageToComponent(message)); } set componentName(value: string) { this.componentTagName = value; this.renderComponentWhenDefined(); } // re-route messages from the component to the event bus onMessageReceivedFromComponent(message: CustomEvent) { this.eventBus.publish(this.messageType, message.detail); } sendMessageToComponent(message: any) { this.messageSender.emit(message); } } =========== Источник: habr.com =========== Похожие новости:
Разработка веб-сайтов ), #_programmirovanie ( Программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 14:05
Часовой пояс: UTC + 5