Dunos
Dunos

Reputation: 189

How to dynamically add component BEFORE another list element in Angular 2+

Let's say I have a list of items, LI style, like a contact list or to-do things like this:

<li data-letter="J">James Taylor</li>
<li data-letter="J">John Connor</li>
<li data-letter="K">Kyle Connor</li>
<li data-letter="P">Peter Parker</li>

And I need to dynamically add a letter separator between some of those elements, where the result should be something like this:

<separator-component>- J -</separator-component>
<li data-letter="J">James Taylor</li>
<li data-letter="J">John Connor</li>
<separator-component>- K </separator-component>
<li data-letter="K">Kyle Connor</li>
<separator-component>- P -</separator-component>
<li data-letter="P">Peter Parker</li>

Using ViewChildren I have managed to get the list of QueryList<ViewContainerRef> for those LI items, and could add the separator components to them, but only after the item I want to insert BEFORE.

This is my component template:

<li #listItems *ngFor="let element of elements" [attr.data-letter]="element.letter">{{ element.name }}</li>

And this is the code (the relevant part of it):

@ViewChildren('listItems', {read: ViewContainerRef}) listItems: QueryList<ViewContainerRef>;

addItem(index: number) {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(SeparatorComponent);
    const componentRef = this.listItems.toArray()[index].createComponent(componentFactory, 0);

    componentRef.changeDetectorRef.detectChanges();

    this.letterRefs.push(componentRef);
}

And this is the result I'm getting so far...

<li data-letter="J">James Taylor</li>
<separator-component>- J -</separator-component>
<li data-letter="J">John Connor</li>
<li data-letter="K">Kyle Connor</li>
<separator-component>- K </separator-component>
<li data-letter="P">Peter Parker</li>
<separator-component>- P -</separator-component>

Any suggestions?

Upvotes: 0

Views: 1503

Answers (1)

Frank Modica
Frank Modica

Reputation: 10516

This seems to work:

<ng-container *ngFor="let element of elements" #listItems>
    <li [attr.data-letter]="element.letter">{{ element.name }}</li>
</ng-container>

Edit: After closer inspection this works because ng-container becomes a comment in the DOM before the li:

<!---->
<li></li>

And createComponent always renders the new component after (not inside) the ViewContainerRef, which is the comment in this case.

So it's kind of like doing the following:

<ng-container *ngFor="let element of elements">
   <ng-container #listItems></ng-container>
   <li [attr.data-letter]="element.letter">{{ element.name }}</li>
</ng-container>

Also, the 0 in this line can be removed because we don't care about an index anymore:

this.listItems.toArray()[index].createComponent(componentFactory, 0);

I actually hope someone has a better answer to this question because it feels like a hack!

Upvotes: 1

Related Questions