【问题标题】:Observe property on an array of objects for any changes观察对象数组的属性是否有任何变化
【发布时间】:2016-10-13 07:01:04
【问题描述】:

我正在使用Aurelia,并且我有一个绑定到网格的项目数组,并且它们有一个选定的属性。我想绑定一个按钮以在任何一项为真时启用。我可以做一个蛮力的方法,我有一个过滤列表并返回所选项目的吸气剂,但这意味着我会在应用程序中不断地进行脏检查,我不想这样做。我希望有一种更有效的方法。有什么想法吗?

【问题讨论】:

    标签: javascript aurelia


    【解决方案1】:

    你可以做的事情很少——假设我的用例是正确的:

    脏检查(这只是一个属性-没什么大不了的)

    export class Item {
      selected = false;
    }
    
    export class ViewModel {
      items = [new Item(), new Item(), new Item()];
    
      get anySelected() {
        var items = this.items, i = items.length;
        while(i--) {
          if (items[i].selected) {
            return true; 
          }
        }
        return false;
      }
    }
    

    观察项目

    import {BindingEngine, inject} from 'aurelia-framework';
    
    export class Item {
      selected = false;
    }
    
    @inject(BindingEngine)
    export class ViewModel {
      items = [new Item(), new Item(), new Item()];    
      anySelected = false;
      subscriptions = [];
    
      constructor(locator) {
        this.bindingEngine = bindingEngine;
      }
    
      updateAnySelected() {
        var items = this.items, i = items.length;
        while(i--) {
          if (items[i].selected) {
            this.anySelected = true;
            return;
          }
        }
        this.anySelected = false;
      }
    
      activate() {
        var items = this.items, i = items.length, observer;
        while(i--) {
          observer = this.bindingEngine.propertyObserver(items[i], 'selected');
          subscriptions.push(observer.subscribe(() => this.updateAnySelected());
        }
        this.updateAnySelected();
      }
    
      deactivate() {
        let dispose;
        while(subscription = subscriptions.pop()) {
          subscription.dispose();
        }
      }
    }
    

    使用集合类

    import {computedFrom} from 'aurelia-framework';
    
    export class Item {
      _selected = false;
    
      constructor(parent) {
        this.parent = parent;
      }
    
      @computedFrom('_selected')
      get selected() {
        return this._selected;
      }
      set selected(newValue) {
        newValue = !!newValue;
        if (newValue === _selected) {
          return;
        }
        _selected = newValue;
        this.parent.itemChanged(newValue);
      }
    }
    
    export class Items {
      items = [];
      selectedCount = 0;
      anySelected = false;
    
      createItem() {
        let item = new Item(this);
        this.items.push(item);
        return item;
      }
    
      itemChanged(selected) {
        this.selectedCount += (selected ? 1 : -1);
        this.anySelected = this.selectCount > 0;    
      }
    }
    
    export class ViewModel {
      items = new Items();
    
      constructor() {
        let item = this.items.createItem();
        item = this.items.createItem();
        item = this.items.createItem();
      }
    }
    

    使用 selectedItems 数组而不是选定的布尔属性

    export class ViewModel {
      items = [{}, {}, {}];
      selectedItems = [];
    
      selectItem(item) {
        this.items.push(item);
      }
    
      deselectItem(item) {
        this.items.splice(this.items.indexOf(item), 1);
      }
    }
    

    出于绑定目的,使用selectedItems.length 作为您的“任何选定”属性

    【讨论】:

    • 太棒了!这里有一些很棒的选择。谢谢。
    • 这有改变吗?
    • 是的...这改变了吗?
    • No-没有改变
    • 感谢出于绑定目的,请使用 selectedItems.length
    【解决方案2】:

    除了 Jeremy 的例子,你还可以创建一个自定义的 setter,例如:

    class Item {
       // this is your ~private~ field
      _isSelected = false;
    
      // in our constructor, we pass the view model and the property name
      constructor(vm, prop, name) {
        this.vm = vm;
        this.prop = prop;
        this.name = name;
      }
    
      get isSelected() {
        return this._isSelected;
      }
      // when you set the value, you increase the vm's property
      set isSelected(value) {
        if (value !== this._isSelected) {
          this.vm[this.prop] += value ? 1 : -1;
          this._isSelected = value;
        }
      }
    }
    
    export class MyViewModel
    {
      items = [];
      itemsSelected = 0; // that's the property we'll pass to the class we've created
    
      constructor() 
      {
        for (let i = 0; i < 50; i++) {
          // instead of adding a annonymous {} here, we add an instance of our class
          this.items.push(new Item(this, 'itemsSelected', `Item ${i+1}`));
        }
      }
    
      toggleIsSelected(item) {
        item.isSelected = !item.isSelected;
      }
    }
    

    我为你创建了一个 plunker:http://plnkr.co/edit/OTb2RDLZHf5Fy1bVdCB1?p=preview


    这样做,您将永远不会循环查看某个项目是否已更改。

    【讨论】:

    • 这与“使用集合类”示例相同,尽管它缺少 isSelected getter 函数上的 @computedFrom('_isSelected') 装饰器,这意味着 isSelected 将被脏检查。
    • 感谢您的帮助!
    【解决方案3】:

    我认为您也可以利用EventAggregator,如here 所示。这样就不需要一直进行脏检查,而是在自己的VM中处理项目选择事件并发布事件数据;另一端的订阅者将听相同的内容并执行所需的体操。

    但是,我从来没有使用过它,所以我不确定它的更深层次的细节。但从文档来看,它看起来很简单。

    【讨论】:

      【解决方案4】:

      杰里米让我想到了这个in this bug。所以看起来你也可以通过自定义绑定行为来刷新绑定。希望杰里米能确认我在这里没有做任何太傻的事情。

      这样使用:

      repeat.for="item of items | filter &amp; array:'propertyName'"

      它会覆盖标准观察行为并观察数组和您在每个项目上定义的属性。它可能可以改进为更通用...

      function observeProperty(obj, property) {
        this.standardObserveProperty(obj, property);
        let value = obj[property];
        if (Array.isArray(value)) {
          this.observeArray(value); 
          for(let each of value){   
            this.standardObserveProperty(each, this.propertyName); 
          }
        }
      }
      
      export class ArrayBindingBehavior {
        bind(binding, source, property) {
          binding.propertyName = property;
          binding.standardObserveProperty = binding.observeProperty;
          binding.observeProperty = observeProperty;
        }
      
        unbind(binding, source) {
          binding.observeProperty = binding.standardObserveProperty;
          binding.standardObserveProperty = null;
          delete binding.propertyName;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-09-07
        • 2014-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-30
        • 1970-01-01
        相关资源
        最近更新 更多