4

I have the following Problem: I am using this *ngFor loop:

<app-sector *ngFor="let subsector of sector.subsectors" [sector]="subsector"></app-sector>

if the Array "subsectors" looks like this:

var subsectors = [
    {text: 'test', title: 'test', id: 'abc'},
    {text: 'test123', title: 'test123', id: 'def'},
    {text: 'test321', title: 'test321', id: 'ghi'}
]

It adds 3 "app-sector"-Components as expected. Those "app-sector"-Components are being added inside an "app-sector"-Component. The parent gets his subsectors from his parent via "@Input" in my "app-component".

This is how the HTML-Structure looks like:

<app-root>
    <app-sector *ngIf="playlist" [sector]="playlist">
        <app-sector *ngFor="let subsector of sector.subsectors" [sector]="subsector"></app-sector>
    </app-sector>
</app-root>

Now here is where the Problem starts: When I update the subsectors like this:

//Add new subsectors
this.subsectors.push({text: 'Blah', title: 'Blah', id: '123'});
this.subsectors.push({text: 'Blah123', title: 'Blah123', id: '456'});
this.subsectors.push({text: 'Blah321', title: 'Blah321', id: '789'});

//Remove old subsectors
this.subsectors.splice(2, 1);
this.subsectors.splice(1, 1);
this.subsectors.splice(0, 1);

The *ngFor doesn't create new Components and only destroys a few. I can't really see a pattern. It seems to decide randomly if it destroys or creates new Components.

Here is what i've tried sofar: Use trackBy. I added a trackBy filter which returned the "id" property but it didn't change anything. Here is how:

<app-sector *ngFor="let subsector of sector.subsectors; trackBy:identify;" [sector]="subsector"></app-sector>

identify(index,item){
    return item.id;
}

then i tried some Tricks i've seen while researching this issue, like using slice instead of splice. Or in the Parent i tried to do this:

ngOnChanges(changes) {
    if(this.sector['subsectors']) {
        setTimeout(() => {
            console.log('spliced subsectores for reinitialization');
            this.sector['subsectors'] = this.sector['subsectors'].slice();
        }, 1000);
    }
}

I hope you can help me! Please ask me in the comments if you need any further information to help me :)

kind regards.

Avejack
  • 59
  • 6
  • 1
    In the *ngFor you iterate over sector.subsectors, but in the code you modify this.subsectors. Maybe i have overlooked something, but this looks peculiar to me. – kodeaben Jun 02 '17 at 07:40
  • it is because i update the subsectors inside the "sector" class :) this = sector – Avejack Jun 02 '17 at 09:12
  • Okay. I don't quite get how you put a list of app-sectors inside another app-sector? I have never seen that done before and cannot figure out how that works? – kodeaben Jun 02 '17 at 10:58
  • well "app-sector" is a component in my app... And i simply put that component inside that component if this sector has some subsectors in it... The Component gets an object called "sector". This may have an non-empty array "subsectors" in it. if this is the case another "app-sector" is created inside the "app-sector" and gets the sector of subsector via @Input. if this contains subsectors the same thing repeat... thats it. – Avejack Jun 02 '17 at 11:14

2 Answers2

0

This is only my two cents but I feel like this is a problem caused by translcusion


What do I mean by this?

In your template you have the following structure :

<app-root>
    <app-sector *ngIf="playlist" [sector]="playlist">
        <app-sector *ngFor="let subsector of sector.subsectors" [sector]="subsector"></app-sector>
    </app-sector>
</app-root>

<app-sector> has contained within its body another <app-sector> this leaves me to assume you have used ng-content somwhere in the template of your app-sector.

I've had similar unexpected results in the past with *ngFor and ng-content.

I usually come across this answer when googling the subject.

Children passed by the parent can only projected once (no matter how many are there. If the elements don't select specific and different parts of the passed children using the select attribute, then everything is projected to the first with the default selector (none).

This would not explain why your initial data is displayed 3 times , but its just my two cents. Hope it can help.

Daniel Cooke
  • 1,426
  • 12
  • 22
  • Thank you for your answer. I haven't used "ng-content". As you can see i am passing the subsector to the child via @Input (->[sector]="subsector"). The "subsector" contains every info the Component "app-sector" needs to be displayed as expected. And there is no problem with that. The only problem is that *ngFor seems to not create new instances and only destroys some instances of "app-sector" when the Array has been updated. – Avejack Jun 02 '17 at 13:23
0

I have solved this. I had to add changeDetection: ChangeDetectionStrategy.OnPush inside my @Component({}). It tells Angular2 to treat any input as if they're immutable. - Solved.

Avejack
  • 59
  • 6