[Fixed] Angular Element – Body strangely getting appended at the end

Issue

This is strange. Whenever I render my custom element, the body (innerHTML) of the element gets appended at the end.

This is my homepage – see ‘childbody’ is in the body of the s-child element –

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: '<s-child>childbody</s-child>'
})
export class AppComponent {
  title = 'test-app';
}

This is my s-child element – Note that I’m not rendering <ng-content> anywhere –

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-child',
  template: '<p>child works!</p>'
})
export class ChildComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }
}

This is the output when I run this app – Note that ‘childbody’ is appended at the end, despite it not being ‘asked to render anywhere’ (i.e the weird part) –

enter image description here

This is my app-module where I declare the custom element – this is just FYI / additional context

import { BrowserModule } from '@angular/platform-browser';
import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule, NO_ERRORS_SCHEMA } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ChildComponent } from './test/child/child.component';
import {createCustomElement} from '@angular/elements';

@NgModule({
  declarations: [
    AppComponent,
    ChildComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [ChildComponent],
  schemas: [
    CUSTOM_ELEMENTS_SCHEMA
  ]
})
export class AppModule {
  constructor(injector: Injector) {

    const child = createCustomElement(ChildComponent, {injector: injector});
    customElements.define('s-child', child);
  }
}

I wanted to provide a stackblitz, but that’s behaving weirdly with custom elements, apologize for that. Feel free to ask any questions. Any help will be appreciated. I’m using angular 10.2.4.

Solution

Ok, after 6.5 hours of head-banging, I figured out a solution –

  1. When the element is used directly within a component – The selector of the corresponding element should be used (not the element tag).

  2. When an element is dynamically inserted, the tag for custom element can be used, possibly with a sanitizer, e.g. –

  constructor(private sanitizer: DomSanitizer) {
  }

  ngOnInit() {
    this.sanitizedData = this.sanitizer.bypassSecurityTrustHtml('<s-child>adsf</s-child>')
  }

Leave a Reply

(*) Required, Your email will not be published