0

Take the angular feature to dynamically add components.

Documentation

Demo

I need help to make it so we show the list of ads directly on the page rather than one after the other.

I thought it would be as simple as adding *ngFor on the ng-template element but it seems more complicated than this.

I'm rather new with angular 2 so any help would be appreciated.

Jahrenski
  • 171
  • 4
  • 20

1 Answers1

4

Change the loadComponent function to this:

loadComponent() {
    let viewContainerRef = this.adHost.viewContainerRef;

    for (const ad of this.ads) {
        let componentFactory = this.componentFactoryResolver.resolveComponentFactory(ad.component);
        let componentRef = viewContainerRef.createComponent(componentFactory);
        (<AdComponent>componentRef.instance).data = ad.data;
    }
}

The viewContainer inserts the views one after the other.

EDIT:

About the error thrown. It's better with a piece of code. And here's a more thorough explanation of the problem: https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

import { ChangeDetectorRef, (...) } from '@angular/core';

constructor(
    private cd: ChangeDetectorRef,
    private componentFactoryResolver: ComponentFactoryResolver
) { }

ngAfterViewInit() {
    this.loadComponent();
    this.getAds();
    this.cd.detectChanges(); 
}
  • It works unexpectedly well except I get this error sometimes with it : Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined' Any idea where this is from? Is it because we change the template in ngAfterViewInit ? – Jahrenski Nov 08 '17 at 21:26
  • 1
    Yes, you are correct. I overlooked that one. It is because of the way Angulars change detection works. Search the error you got, you'll find tons of detailed explanations. But basically the problem is that the current cycle of change detection has already passed when we are changing the view. That could lead to inconsistency between the data in the .ts file and the presented view. A simple solution is to inject the ChangeDetectorRef and use its instance to run a manual change detection cycle. Simply call the .detectChanges() method of its instance in the ngAfterViewInit() after this.getAds() – Dimitar Pechev Nov 08 '17 at 21:51
  • I might be pushing it but I can't seem to find how to bind an event such as click to the ng-template element. Is there a special syntax to use instead of the (event)="myFunc()" form? – Jahrenski Nov 09 '17 at 13:58
  • 1
    You can't bind events to the ng-template tag. It's just a way to show angular where you want your content to be inserted. In fact the ng-template tag is not even present in the dom when the tempate is parsed. You can of course create the event bindings inside the generated component and they will be fired, but there is no direct way to propagate them to the parent component with normal parent-child communication. There are workarounds though of course. One solution is given here: https://stackoverflow.com/questions/36242119/how-can-i-use-an-event-emitter-on-a-dynamically-added-component – Dimitar Pechev Nov 09 '17 at 16:16
  • Perfect! I managed to bind the click event using `componentRef.location.nativeElement.addEventListener( 'click', this.execItemClicked.bind( this, item ) );` I used a BehaviorSubject to stream changes from the parent to the children and everything updates as it should! – Jahrenski Nov 09 '17 at 16:50