I have a normal Angular 10 application with lazy loaded modules and routing. However, I have a special requirement I need to fulfill.
On most pages I want to initialize the full application with routing etc. by embedding the <app-root>
element in the index.html – which is the AppComponent
. On some pages on the other hand not the full app should be initialized, rather only one specific <header-search>
component that I've registered using @angular/elements (web components). This also means that no routing should take place nor should any other component except the <header-search>
be initialized (if it's not embedding by the <header-search>
component itself) in this case.
Side note just for you to understand the background of the use case: In the project I'm building not all parts are decoupled with Angular. Some pages are rendered backend-side using Twig/PHP. But I need the search functionality in the header that was built with Angular to be available on these pages too. This means I won't have the full application available at the same time, only the HeaderSearchComponent
in this case. On other pages, however, the full application will be initialized including the HeaderSearchComponet
, so there's no need for a separate embed using web components – in this case the
<app-root>
element is enough.
My thoughts where do register the HeaderSearchComponent
as <header-search>
custom web component using angular elements like:
@NgModule({
imports: [BrowserModule, FormsModule, SharedModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
entryComponents: [HeaderSearchComponent]
})
export class AppModule implements DoBootstrap {
constructor(injector: Injector) {
const webComponent = createCustomElement(HeaderSearchComponent, {
injector
});
customElements.define("header-search", webComponent);
}
public ngDoBootstrap(): void {}
}
With that I should be able to render the HeaderSearchComponent
using <header-search>
or the full app using <app-root>
. However, once I only embed <header-search>
without having <app-root>
available at the same time Angular throws an error:
Error: The selector "app-root" did not match any elements
You can try this out yourself in the following minimal Stackblitz example without complex application logic or routing, by replacing <app-root></app-root>
with <header-search></header-search>
in the index.html
file.
https://stackblitz.com/edit/angular-ivy-a4ulcc?file=src/index.html
As mentioned, what I need is a working component <header-search>
without the <app-root>
element being present. But also, it should be possible to have the <app-root>
element for the full application without the <header-search>
component being present. So it's either header-search or app-root, both should work.
How to have a custom element component (in this case <header-search>
) as an entry point and still the possibility to initialize a full Angular application with the <app-root>
element?
Angular web components already work with angular elements. The problem that I've had so far is that I couldn't have only a web component without an <app-root>
element at the same time. However, removing the boostrap
property of the app module entirely and instead adding
entryComponents: [AppComponent],
will make the error disappear. However, this will not initialize <app-root>
anymore, since we've removed it from the boostrap
part. Now we have to conditionally manually bootstrap the app with:
public ngDoBootstrap(appRef: ApplicationRef): void {
if (document.querySelector('app-root')) {
appRef.bootstrap(AppComponent);
}
}
This will do the trick.
For me, it seems like you forgot to add HeaderSearchComponent
in the declarations
part of your app.module.ts
.
Probably try it like this:
@NgModule({
declarations: [
AppComponent,
HeaderSearchComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
GraphQLModule,
SharedModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent],
entryComponents: [HeaderSearchComponent],
})
export class AppModule {
constructor(private injector: Injector) {
const webComponent = createCustomElement(HeaderSearchComponent, {injector});
customElements.define('header-search', webComponent);
}
}
Also make sure your app.component.ts
has the following annotation:
@Component({
selector: 'app-root',
...
})
export class AppComponent { ... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With