2016-02-13 56 views
4

在这个简单的Angular2部件请看:如何脏检查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有一个属性特征,这是由另一组件(例如SettingsPanel)改性。这些组件仅通过服务链接(无输入参数)。

因此,正如您所看到的,我只需要从_settings.features属性中只提取并标记为状态栏兼容功能。

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

然而,正如你可能已经猜到,角投在开发模式下的例外:它检查

这是因为_.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; 
    } 
} 

这样角是幸福的,但我不知道。

回答

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

+0

好的解决方案!我试图使用memoize,但我尝试了错误的方式。我没有提供缓存键功能。 –

0

只有当过滤条件或数组项目发生更改时,才存储过滤的数组和过滤条件,并重新创建过滤数组,并始终返回存储的数组。

另一种方法是改改检测到onPush并通过注入ApplicationRef,并呼吁明确通知角约改变其.tick()

1

返回相同的阵列(参考)各一次。

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()吸气功能将被调用每个变化检测周期。这可能会导致(CPU)昂贵。无论何时添加/删除/更改功能,您可能会更好(重新)在服务中生成过滤列表。然后您的StatusAreaComponent可以简单地在ngOnInit()中获得对该滤波阵列的引用。这可能是我如何实现这一点。

+0

该解决方案将打破更改检测。因为在每个检测器中都会认为这个特性没有改变,组件视图也不会被更新。 –

+0

@TimofeyYatsenko,我假设模板中有一个'NgFor'循环。如果是这样,更改检测将检查数组中每个项目的绑定,因此即使数组(引用)未更改,视图也会更新。这肯定有一些常见的困惑......请参阅[这个答案](http://stackoverflow.com/a/35106215/215945)了解更多信息。 –