Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 6: Why is child component template rendered twice?

I have two Angular components app-image-input and app-button. I use app-button in the template for app-image-input as follows.

AppButtonComponent

appButton.html

<button type="button" (click)="buttonClicked.emit()">{{label}}</button>

appButton.ts

@Component({
  selector: 'app-button',
  templateUrl: './appButton.html'
})
export class AppButtonComponent {

  @Input()
  label : string

  @Input()
  enabled : boolean

  @Output()
  buttonClicked = new EventEmitter()
}

AppImageInputComponent

appImageInput.html

<div class="take-photo">
  <span>Photo</span><input type="file" accept="image/*" capture>
</div>

<hr class="hr">

<div class="inner">
    <app-button class="submit" label="Submit" (buttonClicked)="submitClk()"></app-button>
    <app-button label="Cancel" (buttonClicked)="cancelClk()"></app-button>
</div>

AppModule

app.module.ts

// ..... imports go here.

@NgModule({
  declarations: [AppButtonComponent, AppImageInputComponent],
  entryComponents: [AppButtonComponent, AppImageInputComponent],
  imports: [
    FormsModule,
    BrowserModule,
  ],
  providers: [],
  bootstrap: []
})
export class AppModule { 
  ngDoBootstrap() {
  }

  constructor(injector: Injector) {
    Array.from(new Map<Type<any>, string>([
      [AppButtonComponent, 'app-button'],
      [AppImageInputComponent, 'app-image-input'],
      ]), ([key, value]) => {
      customElements.define(value, createCustomElement(key, {injector}))
    })
  }
}

Problem I package my app as angular elements and use in vanilla HTML/JS app. The weird behavior I am encountering has to do with the way I use my image input component.

If I do the following, I get an image component as expected.

<body>
  <app-image-input></app-image-input>
</body>
</html>

enter image description here

However, when I do the following, buttons are duplicated.

<body>
  <script>
    setTimeout(() => {
      var elem = document.createElement("app-image-input");
      document.body.appendChild(elem);
    }, 1000);
  </script>
</body>
</html>

enter image description here

Please note that setting the timeout to something around 400 (instead of 1000) millisecs gives the correct output!!

You can find complete project here.

Also, https://stackblitz.com/edit/angular-4mgv3c thanks to Caramiriel

like image 846
Samer Makary Avatar asked Jun 22 '19 13:06

Samer Makary


1 Answers

As mentioned above, it's rendered as an Angular component and it's also rendered as an web component too. Hence, you see it twice. Just use an custom element name that does not match the app's selector, so that you can decide whether to render the custom element version or the Angular component version.

like image 110
Manfred Steyer Avatar answered Oct 21 '22 13:10

Manfred Steyer