Search code examples
angularangular-directive

Custom structural directive creates too many elements


I created my custom structural directive which should render the host element for each element in the passed array of items:

@Directive({
  selector: '[appItems]'
})
export class ItemsDirective implements OnInit, OnChanges {

  @Input('appItemsOf')
  items: any[];

  constructor(private container: ViewContainerRef,
              private template: TemplateRef<any>) {
  }

  ngOnInit(): void {
    this.container.createEmbeddedView(this.template);
  }

  ngOnChanges(): void {
    this.container.clear();

    for (const item of this.items) {
      if (item) {
        this.container.createEmbeddedView(this.template, {
          $implicit: item,
          index: this.items.indexOf(item)
        });
      }
    }
  }
}

Usage:

<div *appItems="let item of items">
  Item: {{ item.name }}
</div>

Which, unfortunately, prints:

Item: 1
Item: 2
Item: 3
Item:

The last element is being created and rendered although the item array looks like:

items = [
    {
        name: '1'
    },
    {
        name: '2'
    },
    {
        name: '3'
    }
]

I created a https://stackblitz.com/edit/angular-qwatckstackblitz showing this issue.

Why is this so and how should it be done properly?


Solution

  • Currently, the following is done:

    1. Directive instance is created
    2. NgOnChanges is invoked
    3. NgOnInit is invoked

    So basically, once you create the the N-views for your collection, you create a further one during NgOninit, resulting in N+1 embedded views in your template being generated.

    Simply remove the content of NgOnInit.