对 ngFor 指令使用方法调用而不是 属性 时无限加载

Endless load when using method call instead of property for ngFor directive

在我的模板中,我有这段代码:

<a *ngFor="let item of navItems"
   [routerLink]="item.link"
   routerLinkActive="active"
   class="navigation-item"
   [ngClass]="{'enabled': item.enabled}"
>
  <span class="color-{{item.color}}">
    <mat-icon>{{item.icon}}</mat-icon>
  </span>
  <div class="label">{{item.label}}</div>
</a>

其中 navItems 是我的组件中的一个数组:

navItems: NavItem[] = [
  {
    link: 'link1', label: 'Label 1', icon: 'thumb_up', color: 'green', enabled: true
  },
  {
    link: 'link2', label: 'Label 2', icon: 'thumb_down', color: 'red', enabled: true
  },
  {
    link: 'link3', label: 'Label 3', icon: 'insert_drive_file', color: 'blue', enabled: true
  },
  {
    link: 'link4', label: 'Label 4', icon: 'note_add', color: 'blue', enabled: true
  },
];

这很好用。现在,我需要对此进行更改,以便 navItems 可以动态更改。我试过像这样将 navItems 属性 转换为 getter:

get navItems(): NavItem[] {
  return [
    {
      link: 'link1', label: 'Label 1', icon: 'thumb_up', color: 'green', enabled: true
    },
    {
      link: 'link2', label: 'Label 2', icon: 'thumb_down', color: 'red', enabled: true
    },
    {
      link: 'link3', label: 'Label 3', icon: 'insert_drive_file', color: 'blue', enabled: true
    },
    {
      link: 'link4', label: 'Label 4', icon: 'note_add', color: 'blue', enabled: true
    },
  ];
}

但是,一旦我这样做,浏览器选项卡就会在加载组件时陷入无休止的加载循环,必须通过任务管理器终止 - 我没有得到控制台输出,什么也没有。

我也尝试使用普通方法调用而不是 getter 来提供数组,结果相同

我返回的数组仅由具有直接分配的字符串和布尔文字的普通对象组成,因此不会在那里发生进一步的调用,所以不太可能只是我忽略了一个递归循环。

我是不是做错了什么?不能使用方法调用/getters 来提供 ngFor 指令的项目吗?

在这里,当我使用时,trackBy 解决了问题

In html file

<a *ngFor="let item of navItems; trackBy: trackByFn" [routerLink]="item.link" routerLinkActive="active"
class="navigation-item"
[ngClass]="{'enabled': item.enabled}"
>
<span class="color-{{item.color}}">
 <mat-icon>{{item.icon}}</mat-icon>
</span>
<div class="label">{{item.label}}</div>
</a>

在ts文件中

export class AppComponent {
   trackByFn(index,item){
     console.log(index,"n:",item);
     return index;
   }
  get navItems(): NavItem[] {
    return [
      {
        link: 'link1', label: 'Label 1', icon: 'thumb_up', color: 'green', enabled: true
      },
      {
        link: 'link2', label: 'Label 2', icon: 'thumb_down', color: 'red', enabled: true
      },
      {
        link: 'link3', label: 'Label 3', icon: 'insert_drive_file', color: 'blue', enabled: true
      },
      {
        link: 'link4', label: 'Label 4', icon: 'note_add', color: 'blue', enabled: true
      },
    ];
  }
}
class NavItem {
  link;
   label;
    icon;
     color;
     enabled;
}

注:

案例 1:没有 routerlinkactive[rla] 指令和 trackBy 工作正常

案例 2:rla 和 trackBy 工作正常

case-3 : 有 rla 而没有 trackBy, --> Loop

Here, RouterLinkActive directive implements [AfterContentInit,and others] inside this AfterContentInit there is a method call update()...which is responsible for having this continuous loops.. I believe that, when the method returns the array..first the elements will get created and then the directive starts acting upon it.. [FYI: When update() was commented out and with out trackBy ,this too works fine as it can't excute the hook methods.]

如果您观察方法调用 navItems ,方法调用数 = 数组中的项目数。 每次返回数组时,它都会呈现这些值,指令将作用于现有元素并且指令具有钩子 [AfterContentInit,onDestroy] 负责添加 class 元素和销毁。

Hence in the absence of trackBy, on each method return , elements will be created and directives will be acting on it by attaching hooks to it on the next method call again, they will be consider new , and the directives will do the same , but destroys the previous elements.. this way it is going in the loop...

希望对您有所帮助!!!

参考:https://netbasal.com/angular-2-improve-performance-with-trackby-cc147b5104e5

参考:使用来自 https://github.com/angular/angular

的源代码创建用于调试的 CustomRouterLinkActive

我认为这里的问题是当你在实例变量中有数组时,数组总是相同的。我的意思是,每次 Angular 尝试检测更改时,它检测到数组是相同的,因此它没有执行进一步的操作。

但是,一旦将实例变量更改为 getter,每次调用 navItems 时都会 return 创建一个新数组。在这种情况下,每次 Angular 进行变化检测时,都会发现一个不同的值。从概念上讲,这些项目是相同的并不重要。数组是不同的对象。

我的意思是,之前:

// if comp is an instance of your component:
const a = comp.navItems;
const b = comp.navItems;
console.log(a === b); // true. a and b are pointers to the same object.

但是在你的改变之后:

const a = comp.navItems; // assigning a the return value of a func!
const b = comp.navItems: // assigninb b the return of a NEW call of a func
console.log(a === b); // false. Two arrays were created here. Two objects. 

为避免此问题,您应该按照上面的建议使用 trackByFunction。这样只有 new/modified 项会再次呈现。您也不应该每次 return 一个新数组,只有当数据确实发生变化时。如果你仍然想在 getter 中动态构造数组,请考虑在你的组件中使用 ChangeDetectionStrategy.OnPush,然后使用 ChangeDetectorRef.markForCheck() 来指示组件数据已更改,应该是在更改检测期间检查。如果您不这样做,Angular 将永远不会重新呈现组件,除非它的任何输入属性发生变化。