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

时间:2018-03-04 14:46:59

标签: angular typescript angular2-directives ngfor

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

<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来提供数组,结果相同

我正在返回的数组只是由直接指定字符串和布尔文字的普通对象组成,因此不会再发生进一步的调用,所以它不太可能只是我的一个递归循环。

我做错了吗?你不能使用方法调用/ getter来提供ngFor指令的项吗?

2 个答案:

答案 0 :(得分:1)

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

  

在html文件中

<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;
}

注意:

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

案例2:使用rla和trackBy正常工作

案例-3:使用rla且没有trackBy, - &gt;环

  

这里,RouterLinkActive指令实现[AfterContentInit,和   其他]在这个AfterContentInit里面有一个方法调用   update()...负责这个连续的循环..我   相信,当方法返回数组时...首先是元素   将被创建,然后指令开始采取行动.. [仅供参考:   当update()被注释掉并且没有trackBy时,这也有效   很好,因为它不能执行钩子方法。]

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

  

因此,在没有trackBy的情况下,在每个方法返回时,元素将会   创建并通过附加钩子指令将作用于它   它再次调用下一个方法,他们会考虑新的,而且   指令也会这样做,但会破坏以前的元素。   这样它就会循环......

希望这有帮助!!!

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

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

中的来源创建CustomRouterLinkActive以进行调试

答案 1 :(得分:1)

我认为这里的问题是当你在实例变量中使用数组时,数组总是相同的。我的意思是,每当Angular试图检测到变化时,就会发现阵列是相同的,所以它没有进行进一步的操作。

但是,一旦将实例变量更改为getter,每次调用navItems时都会返回一个新数组。在这种情况下,每当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,如上所述。这样只会再次渲染新的/修改过的项目。你也不应该每次都返回一个新的数组,只有当数据确实发生了变化时。如果您仍想在getter中以dinamically方式构造数组,请考虑在组件中使用ChangeDetectionStrategy.OnPush,然后使用ChangeDetectorRef.markForCheck()指示组件数据已更改,并且应在更改检测期间检查它。如果你不这样做,Angular永远不会重新渲染组件,除非它的任何输入属性发生变化。