【问题标题】:How to dirty-check iterable component property in Angular2?如何脏检查Angular2中的可迭代组件属性?
【发布时间】:2016-02-13 08:45:12
【问题描述】:

看看这个简单的Angular2组件:

@Component({
  selector: 'status-area',
  directives: [],
  template: require('./status-area.tpl.jade')
})

export class StatusAreaComponent {

  constructor(private _settings: SettingsProvider) {}

  get items() {
     return _.filter(this._settings.features, (feature: any) => {
      return feature.inStatusBar && feature.isEnabled;
    });
  }
}

SettingsProvider 有一个属性features,它被另一个组件(例如 SettingsPanel)修改。此组件仅通过服务链接(无输入参数)。

所以,正如您所见,我只需要从 _settings.features 属性中获取已启用并标记为状态栏兼容的功能。

为此,我使用 lodash 方法查找。

不过,你可能猜到了,angular 在开发模式下会抛出异常:

检查后表达式发生了变化

这是因为_.filter() 每次传递都返回数组的新实例,但集合是相同的。

我的问题是,您如何解决这种情况?或者可能有另一种解决方案/方法来处理这种情况。

在我看来,如果 angular2 有这样的特殊装饰器会非常有用:

@checkIterable()
get items() {
     return _.filter(this._settings.features, (feature: any) => {
      return feature.inStatusBar && feature.isEnabled;
    });
  }

当然,我可以通过使用事件和事件侦听器的另一种方法来解决这个问题,但它产生了很多样板代码。

看看这个例子:

export class StatusAreaComponent implements OnInit, OnDestroy {

  protected _items;
  protected _removeWatcherFn;

  constructor(private settings: SettingsProvider) {}

  ngOnInit() {
    this._items =  this._fetchItems();

    this._removeWatcherFn = this.settings.onChange.bind(() => {
      this._items =  this._fetchItems();
    });
  }

  ngOnDestroy() {
    this._removeWatcherFn();
  }

  _fetchItems() {
    return _.filter(this.settings.features, (feature: any) => {
      return feature.inStatusBar;
    });
  }


  get items() {
    return this._items;
  }
}

这样 Angular 很高兴,但我不高兴。

【问题讨论】:

    标签: typescript angular


    【解决方案1】:

    由于您已经在使用 lodash,您可以使用方便的_.memoize 方法来缓存过滤结果。它通常用于避免昂贵的计算,但在您的情况下,您可以从它的副作用中受益,因为 Angular 不会有这个问题,过滤数组是每次检查的不同对象。

    以下是问题的可能解决方案:

    @Component({
      selector: 'status-area',
      directives: [],
      template: require('./status-area.tpl.jade')
    })
    export class StatusAreaComponent {
    
      constructor(private _settings: SettingsProvider) {
        this.initFilter();
      }
    
      initFilter() {
    
        function filterItems(items) {
          return _.filter(items, (feature: any) => {
            return feature.inStatusBar && feature.isEnabled;
          })
        }
    
        this.filterFeatures = _.memoize(filterItems, function(items) {
          return filterItems(items).map(item => item.id).join();
        });
      }
    
      get items() {
        return this.filterFeatures(this._settings.features);
      }
    
    }
    

    演示: http://plnkr.co/edit/MuYUO3neJdTs0DHluu0S?p=info

    【讨论】:

    • 很好的解决方案!我曾尝试使用 memoize,但我以错误的方式尝试了它。我没有提供缓存键功能。
    【解决方案2】:

    您可以尝试使用“ChangeDetectorRef”类及其“detectChanges”方法。

    也许这个问题可以帮助你:

    【讨论】:

      【解决方案3】:

      每次返回相同的数组(引用)。

      export class StatusAreaComponent {
        filteredItems = [];
        constructor(private _settings: SettingsProvider) {}
        get items() {
          let filteredItems = this._settings.features.filter( (feature: any) => {
            return feature.inStatusBar && feature.isEnabled;
          });
          this.filteredItems.length = 0;
          this.filteredItems.push(...filteredItems);
          return this.filteredItems;
        }
      }
      

      请注意,...ES2015 feature

      我假设您的模板中有 *ngFor="#item of items">{{#item}} 之类的内容。如果是这样,您的items() getter 函数将在每个更改检测周期被调用。这可能会使(CPU)变得昂贵。每当添加/删除/更改功能时,您最好(重新)在服务中生成过滤列表。然后你的StatusAreaComponent 可以简单地在ngOnInit() 中获得对该过滤数组的引用。我大概就是这样实现的。

      【讨论】:

      • 此解决方案将破坏更改检测。因为在每个检查检测器中都会认为这个属性没有改变,组件视图也不会更新。
      • @TimofeyYatsenko,我假设模板中有一个 NgFor 循环。如果是这样,更改检测将检查数组中每个项目的绑定,因此即使数组(引用)没有更改,视图也会更新。这肯定有一些常见的混淆......有关更多信息,请参阅this answer
      【解决方案4】:

      存储过滤后的数组和过滤条件,仅当过滤条件或数组项发生变化时重新创建过滤后的数组,并始终返回存储的数组。

      另一种方法是将更改检测更改为onPush,并通过注入ApplicationRef 并调用其.tick() 来明确通知Angular 更改

      【讨论】:

        猜你喜欢
        • 2011-02-10
        • 2018-03-04
        • 2011-03-04
        • 2017-05-07
        • 1970-01-01
        • 2016-08-06
        • 2012-12-29
        • 2012-03-14
        • 1970-01-01
        相关资源
        最近更新 更多