J'utilise Aurelia et j'ai un ensemble d'éléments liés à une grille et ils ont une propriété sélectionnée sur eux. Je souhaite lier un bouton pour être activé lorsque l'un des éléments est vrai. Je peux faire une approche de la force brute où j'ai un getter qui filtre la liste et renvoie les éléments sélectionnés, mais cela signifie que je ferais des contrôles sales constamment dans l'application et je ne veux pas le faire. J'espère une approche plus efficace. Des idées?
Peu de choses que vous pourriez faire – en supposant que j'ai votre cas d'utilisation correct:
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
au lieu d'un objet booléen sélectionné export class ViewModel { items = [{}, {}, {}]; selectedItems = []; selectItem(item) { this.items.push(item); } deselectItem(item) { this.items.splice(this.items.indexOf(item), 1); } }
À des fins de liaison, utilisez selectedItems.length
comme votre propriété "quelconque sélectionnée"
En plus des exemples de Jeremy, vous pouvez créer un setter personnalisé, par exemple:
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; } }
J'ai créé un plongeur pour vous: http://plnkr.co/edit/OTb2RDLZHf5Fy1bVdCB1?p=preview
Si vous faites cela, vous ne verrez jamais si un élément a changé.
Je pense que vous pouvez également exploiter EventAggregator
comme indiqué ici . De cette façon, il n'est pas nécessaire d'effectuer des vérifications sales tout le temps et à la place gérer l'événement de sélection d'élément dans sa propre machine virtuelle et publier les données d'événement; L'abonné de l'autre côté va écouter les mêmes et effectuer la gymnastique nécessaire.
Cependant, je ne l'ai jamais utilisé, alors je ne suis pas sûr des détails plus profonds. Mais de la documentation, il semble très simple.
Jeremy m'a fait réfléchir à ce problème . Il semble donc que vous pouvez également obtenir le rafraichissement de liaison via des comportements de liaison personnalisés. J'espère que Jeremy peut confirmer que je ne fais rien de trop idiot ici.
Utilisé comme ceci:
repeat.for="item of items | filter & array:'propertyName'"
Il remplace le comportement d'observation standard et observe le tableau et la propriété que vous définissez sur chaque élément. On peut probablement améliorer pour être plus générique …
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; } }