Reputation: 21
I have a use-case, where I should render an external angular component or web-component in an angular application.
I have created a sample application (stackblitz) to reproduce the scenario and below is the visual expecation.
In the sample code, to compile a angular component I use
<div style="margin:20px; display: flex; max-height: 500px;">
<div style="max-width:250px; overflow-y: scroll;">
<button (click)="onInternalControl()">Internal Component</button>
<ng-template #internalTemplate>this is internal template</ng-template>
</div>
<div style="width:250px; height: 150px; overflow-y: scroll;">
<button (click)="onExternalControl()">External Component</button>
<ng-template #externalTemplate>this is external template</ng-template>
</div>
</div>
The below is the component code
export class AppComponent implements OnInit {
@ViewChild('internalTemplate', { read: ViewContainerRef })
internalTemplate: any;
@ViewChild('externalTemplate', { read: ViewContainerRef })
externalTemplate: any;
constructor() {}
ngOnInit() {}
onInternalControl() {
this.internalTemplate.clear();
this.internalTemplate.createComponent(LabelComponent);
}
onExternalControl() {
this.externalTemplate.clear();
// how to dynamically register and load web-component in the ng-template (externalTemplate)
// e.g., assets/external-label.js
}
}
The below code is the web component which is exepected to be render on click of EXTERNAL_COMPONENT button.
class ExternalLabel extends HTMLElement {
// The browser calls this method when the element is
// added to the DOM.
connectedCallback() {
let $p = document.createElement('p');
$p.style.backgroundColor = 'red';
$p.style.width = '250px';
$p.style.height = '250px';
$p.innerHTML = 'External Label Control';
this._shadowRoot.appendChild($p);
}
}
// Register the ExternalLabel component using the tag name <ext-label>.
customElements.define('ext-label', ExternalLabel);
How can I dynamically register and load the external label web component?
Upvotes: 2
Views: 61
Reputation: 57986
I think there is some problem in your web component, here is my web component that works fine.
customElements.define(
'fancy-tabs',
class extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// Attach a shadow root to <fancy-tabs>.
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>#tabs { ... }</style> <!-- styles are scoped to fancy-tabs! -->
<div id="tabs">...</div>
<div id="panels">...</div>
`;
}
}
);
When it comes to web components, make sure you import the script in the scripts array of angular.json.
...
"options": {
"assets": ["src/app/assets"],
"index": "src/index.html",
"browser": "src/main.ts",
"outputPath": "dist/demo",
"scripts": ["src/app/assets/externalLabel.js"],
"styles": ["src/global_styles.css"],
"tsConfig": "tsconfig.app.json"
}
...
Then the web-component, is best to exist in a wrapper component, because we can use CUSTOM_ELEMENTS_SCHEMA
to prevent the error that the selector is unknown. This can also be done at the module level, but why I recommend this is because the schema will block some errors, so your angular application will not show errors properly.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@Component({
selector: 'app-external',
standalone: true,
imports: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `test<fancy-tabs></fancy-tabs>`,
})
export class ExternalComponent {}
Finally we can render this wrapper component as the way we do our normal component.
import { Component, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import { LabelComponent } from './label.component';
import { ExternalComponent } from '../external/external.component';
@Component({
selector: 'my-app',
standalone: true,
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
@ViewChild('internalTemplate', { read: ViewContainerRef })
internalTemplate: any;
@ViewChild('externalTemplate', { read: ViewContainerRef })
externalTemplate: any;
constructor() {}
ngOnInit() {}
onInternalControl() {
this.internalTemplate.clear();
this.internalTemplate.createComponent(LabelComponent);
}
onExternalControl() {
this.externalTemplate.clear();
this.externalTemplate.createComponent(ExternalComponent);
// how to dynamically register and load web-component in the ng-template (externalTemplate)
// e.g., assets/external-label.js
}
}
Upvotes: 0