question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

IVY: ngFor weird behaviour

See original GitHub issue

🐞 bug report

Affected Package

@angular/*

Is this a regression?

Dont know if it worked with IVY before, but it is working without IVY.

Description

NgFor removes elements even thou they are not removed from array. Also removing all items from array, removes animation.

🔬 Minimal Reproduction

git clone https://github.com/kukjevov/ivy-ngfor-error.git cd ivy-ngfor-error npm install npm run server

open Chrome with URL http://localhost:8888

first on notifications page http://localhost:8888

  • under Local notifications click several times on buttons (except Clear Message)
  • several notifications are displayed below
  • if you click on any of notifications only clicked notification should be removed
  • all notifications below clicked notification are removed (event thou in array of notifications only one notification is removed)
  • also if you click on Clear Message all messages are removed without animation

second on select page http://localhost:8888/select

  • options are loaded after timeout and displayed using ngFor
  • assertion fails with error “Index expected to be less than 4 but got 25”
  • options are loaded using ContentChildren
  • this assertion does not fail if options are provided staticaly, or using not delayed ngFor as you can see http://localhost:8888/static

🔥 Exception or Error

🌍 Your Environment

Angular Version:

Angular: 8.1.0-next.2 Browser: any Node: 11.6.0 NPM: 6.9.0

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
alexzuzacommented, Jun 21, 2019

Hi,

I’ve looked at your reproduction and created minimal reproducible scenarios:

Case 1:

import { Component, Directive, ViewContainerRef, Output, 
  EventEmitter, ComponentFactoryResolver, HostListener, Input
} from '@angular/core';

let id = 0;

@Component({
  selector: 'my-app',
  template: `
    <button (click)="add()">Add</button>
    <div>
      <ng-container *ngFor="let item of items; let i = index;" >
        <ng-template [messageRenderer]="item" (removing)="removeAtIndex(i)"></ng-template>
      </ng-container>
    </div>
  `
})
export class AppComponent {
  items = [];

  add() {
    this.items.push(id++);
  }

  removeAtIndex(i) {
    this.items.splice(i, 1)
  }
}

@Component({
  selector: 'ng-message',
  template: `message {{ text }} <span (click)="clicked.emit()">✖</span>`
})
export class MessageComponent {
  @Input() text: string;

  @Output() clicked = new EventEmitter();
}

@Directive({
  selector: '[messageRenderer]'
})
export class MessageRenderer {
  @Input() messageRenderer;

  @Output() removing = new EventEmitter();

  constructor(private vcRef: ViewContainerRef, private resolver: ComponentFactoryResolver) { }

  ngOnInit() {
    const factory = this.resolver.resolveComponentFactory(MessageComponent);
    const componentRef = this.vcRef.createComponent(factory);
    componentRef.instance.text = this.messageRenderer;
    componentRef.instance.clicked.subscribe(() => this.removing.emit());
  }
}

Demo https://ivy.ng-run.com/edit/n4DOKUHyA9izdnfQTr2X

  • click several times on Add button
  • remove the first message by clicking on ✖

It will also remove all notifications below

Notice that it uses ng-template inside ngFor loop.

@kara Seems it has something to do with unexpected behavior in walkTNodeTree function https://github.com/angular/angular/blob/87168acf3928f43f1048a6458de57d85849ea276/packages/core/src/render3/node_manipulation.ts#L83

Case 2:

@Component({
  selector: 'my-app',
  template: `
    <ng-select (queryListChanged)="someComp.doSomething()">
    	<div #x *ngFor="let item of items"></div>
    </ng-select>
    <some-comp #someComp></some-comp>
    <button (click)="test()">Test</button>
  `
})
export class AppComponent { 
  items = [];

  test() {
    this.items = [1, 2, 3];
    markDirty(this)
  }
}

@Component({
  selector: 'ng-select',
  template: `<ng-content></ng-content>`,
})
export class SelectComponent implements AfterViewInit {
  @ContentChildren('x') queryList: QueryList<any>;

  @ContentChildren('y') queryList2: QueryList<any>;

  @Output() queryListChanged = new EventEmitter();

  ngAfterViewInit() {
    this.queryList.changes.subscribe(() => {
      this.queryListChanged.emit();
    })
  }
}

@Component({
  selector: 'some-comp',
  template: `Some component`,
})
export class SubChild {
  @ViewChild('x', { static: true }) x;

  constructor(private cdRef: ChangeDetectorRef) { }

  doSomething() {
    this.cdRef.detectChanges();
  }
}

Demo https://ivy.ng-run.com/edit/7PEjtK2lAoET6Ge3z67B

  • click on Test button
  • look at how the console error “Uncaught Error: ASSERTION ERROR: index expected to be a valid data index” is being araised

The problem here is that currentQueryIndex is being changed during query changes.

Here’s how SelectComponent is being processing during change detection cycle:

🡇 refreshContentQueries

  • refresh queryList => this.queryList.changes.subscribe =>SubChild::cdRef.detectChanges
  • refresh queryList2 => results in error since we’ve changed currentQueryIndex by calling detectChanges in another component that has a query

Seems we can fix it by calling markDirty instead of cdRef.detectChanges in SubChild

0reactions
angular-automatic-lock-bot[bot]commented, Sep 15, 2019

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Angular2 weird form behavior with *ngFor - Stack Overflow
Is there a way to avoid this weird behavior using standard Template Driven Forms ? Or do I have to go through ReactiveForms...
Read more >
NgForOf - Angular
The ngForOf directive is generally used in the shorthand form *ngFor . ... odd: boolean : True when the item has an odd...
Read more >
How to loop through object properties with ngFor in angular
Iterate throw all keys, and push each one into the array you created. Then Put the property name as a value of another...
Read more >
Handling Observables with NgFor and the Async Pipe
In this article you'll learn how to use Observables with Angular's NgFor directive and the async pipe. NgFor has a not-so-obvious feature that ......
Read more >
Most Important Angular 8 Interview Questions with Answers
Angular 8 has some advanced level unique features such as Ivy rendering Engine, ... First, Last, odd, and even are exported by ngFor...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found