[英]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: 你可以做的事很少 - 假设我的用例正确:
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
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.