简体   繁体   English

观察对象数组的属性是否有任何更改

[英]Observe property on an array of objects for any changes

I am using Aurelia and I have an array of items bound to a grid and they have a selected property on them. 我正在使用Aurelia ,我有一个绑定到网格的项目数组,并且它们上面有一个选定的属性。 I want to bind a button to be enabled when any one of the items is true. 我想绑定一个按钮,当任何一个项为真时启用。 I can do a brute force approach where I have a getter that is filtering the list and returning the selected items, but that means that I would be doing dirty checking constantly in the app and I don't want to do that. 我可以做一个蛮力的方法,我有一个过滤列表并返回所选项目的getter,但这意味着我将在应用程序中不断进行脏检查,我不想这样做。 I am hoping for a more efficient approach. 我希望有一个更有效的方法。 Any ideas? 有任何想法吗?

Few things you could do- assuming I have your use case right: 你可以做的事很少 - 假设我的用例正确:

dirty-checking (it's only one property- not a big deal) 脏检查(它只是一个属性 - 不是什么大不了的事)

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

observe the items 观察物品

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

use a collection class 使用集合类

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

use a selectedItems array instead of a selected boolean prop 使用selectedItems数组而不是选定的布尔prop

export class ViewModel {
  items = [{}, {}, {}];
  selectedItems = [];

  selectItem(item) {
    this.items.push(item);
  }

  deselectItem(item) {
    this.items.splice(this.items.indexOf(item), 1);
  }
}

for binding purposes, use selectedItems.length as your "any selected" property 出于绑定目的,使用selectedItems.length作为“任何选定的”属性

In addition to the Jeremy's examples, you can create a custom setter, for example: 除了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;
  }
}

I've created a plunker for you: http://plnkr.co/edit/OTb2RDLZHf5Fy1bVdCB1?p=preview 我为你创建了一个plunker: http ://plnkr.co/edit/OTb2RDLZHf5Fy1bVdCB1?p = preview


Doing that, you'll never be looping to see if some item has changed. 这样做,你永远不会循环,看看是否有一些项目发生了变化。

I think you can also leverage EventAggregator as shown here . 我想你也可以利用EventAggregator如图所示这里 In that way there is no need to perform dirty checking all the time and instead handle the item selection event in its own VM and publish the eventdata; 这样就不需要一直执行脏检查,而是在自己的VM中处理项目选择事件并发布eventdata; the subscriber on the other side will listen to the same and perform the gymnastic needed. 另一方的用户将听取相同的声音并进行所需的体操。

However, I have never used it, so I am not sure about the deeper details of it. 但是,我从未使用它,所以我不确定它的更深层细节。 But from the documentation it looks pretty easy. 但是从文档中看起来很容易。

Jeremy got me thinking about this in this bug . Jeremy让我在这个bug中想到了这一点 So it looks like you can also get the binding refreshing via a custom Binding Behaviors. 所以看起来你也可以通过自定义绑定行为获得绑定刷新。 Hopefully Jeremy can confirm I'm not doing anything too silly here. 希望Jeremy可以证实我在这里做的事情太傻了。

Used like this: 像这样使用:

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

It overrides the standard observe behaviour and observes on the array and the property you define on each item. 它会覆盖标准的观察行为,并观察数组和您在每个项目上定义的属性。 It can probably be improved to be more generic... 它可能会被改进为更通用的......

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM