Playground for web components in Angular.
Based on:
-
https://buddy.works/tutorials/building-web-components-with-angular
-
https://indepth.dev/posts/1116/angular-web-components-a-complete-guide
- Install Angular CLI
npm install -g @angular/cli
- Create an empty workspace
ng new ag-web-components --createApplication=false
cd ag-web-components
- the project name that will contains all our web components
ng generate application WebComponents --skipInstall=true
- Would you like to add Angular routing?
No
- Which stylesheet format would you like to use?
SCSS
ng add @angular/elements
cd projects
cd web-components
cd src
cd app
ng generate component TextForm
(NB. the Component
suffix is added automatically)
@NgModule({
declarations: [
TextFormComponent,
],
imports: [
BrowserModule,
],
entryComponents: [
TextFormComponent
],
// optional: enable the bootstrap to debug the web component
bootstrap: [TextFormComponent],
})
export class AppModule implements DoBootstrap {
constructor(private injector: Injector) {
const webComponent = createCustomElement(TextFormComponent, {injector});
customElements.define('text-form', webComponent);
}
ngDoBootstrap() {}
}
- if in the
index.html
we replaceapp-root
withapp-text-form
, enabling the bootstrap-ing in the NgModule, we can also debug the web-component. Just serve as usual:
ng serve WebComponents
- to be able to customize the web component behaviour via named
slot
, we need to switch the component's encapsulation to ShadowDom. Nb. If not, theslot
content will be appended to the web component.
encapsulation: ViewEncapsulation.ShadowDom,
-
and define a
slot
in the component html:<slot name="header"><p>Default text header</p></slot>
-
Then in the web component instance we can overwrite the default content, for example for a slot named "header":
<text-form>
<span slot="header">My Custom Header</span>
</text-form>
-
to allow the web component consumer to customize the css, we should expose the allowed elements to be customized via the
part
declaration. NB. I just used the same nameheader
here forpart
andslot
, but you can use any name you want and they must not match. -
part
allow to style only that specific element, and doesn't allow to style his children. For that, take a look toexportparts
and::theme
. For example: https://meowni.ca/posts/part-theme-explainer/
<slot name="header" part="header"><p>Default text header</p></slot>
- and then customize the instances via css
<style>
#ctrl::part(header) {
font-size: 32px;
}
</style>
<body>
<text-form id="ctrl"></text-form>
</body>
- Build the web component project
ng build WebComponents --configuration production --output-hashing none --output-path usage-vanilla/dist-static
- install the serve package
npm install -g serve
- Navigate inside the
usage-vanilla
folder
cd usage-vanilla
Serve the index.html
serve
- Open
http://localhost:5000
in a browser
ng generate application UsageAsAgComponent --skipInstall=true
- in
app.module.ts
addTextFormComponent
indeclarations
- use it using the angular selector (NB. not the web component name)
- build or serve the Angular app
ng build UsageAsAgComponent
ng serve UsageAsAgComponent
- Build the web component project
- Include the web component javascript output in angular.json > scripts > main.js
Then you can use the web component in two different ways:
1- You can use the web component in Angular Template, but you need to use ElementRef
, addEventListener
, in addition of some "manual handling" to listen to events. Based on https://stackoverflow.com/a/41610950/3512682
2- My preferred way is to use an Angular directive
to wrap the web component and use it as a Angular Form Control
and benefit of the native Angular framework functionality, as validators, ngModel, etc.
Based on https://coryrylan.com/blog/using-web-components-in-angular-forms