Соблюдать свойство массива объектов для любых изменений
Я использую 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
как ваше свойство "any selected"
Ответ 2
В дополнение к примерам Джереми вы можете создать настраиваемый сеттер, например:
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 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 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;
}
}
Я создал плункер для вас: http://plnkr.co/edit/OTb2RDLZHf5Fy1bVdCB1?p=preview
Выполняя это, вы никогда не будете зацикливаться, чтобы увидеть, изменился ли какой-либо элемент.
Ответ 3
Я думаю, вы также можете использовать EventAggregator
, как показано здесь. Таким образом, нет необходимости постоянно выполнять грязную проверку и вместо этого обрабатывать событие выбора элемента в своей собственной виртуальной машине и публиковать данные eventdata; абонент с другой стороны будет слушать то же самое и выполнять необходимую гимнастику.
Однако я никогда не использовал его, поэтому я не уверен в более глубоких подробностях. Но из документации это выглядит довольно легко.
Ответ 4
Джереми заставил меня задуматься об этом в этой ошибке. Таким образом, похоже, что вы также можете обновить привязку с помощью пользовательских правил привязки. Надеюсь, Джереми может подтвердить, что я не делаю здесь ничего глупого.
Используется следующим образом:
repeat.for="item of items | filter & 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;
}
}