2015-08-14 10 views
14

Sto usando Aurelia e ho una matrice di elementi legati a una griglia e hanno una proprietà selezionata su di essi. Voglio associare un pulsante per essere abilitato quando uno qualsiasi degli elementi è vero. Posso fare un approccio a forza bruta in cui ho un getter che sta filtrando la lista e restituendo gli elementi selezionati, ma ciò significa che farei controlli sporchi costantemente nell'app e non voglio farlo. Sto sperando in un approccio più efficiente. Qualche idea?Osservare la proprietà su una serie di oggetti per qualsiasi modifica

risposta

32

Poche cose si potrebbe fare- supponendo che hanno il tuo caso d'uso a destra:

sporco controllo (è unico a non proprietà- un grosso problema)

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

osservare gli elementi

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

utilizzare una classe di raccolta

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

utilizzare un array selectedItems invece di un valore booleano selezionato prop

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

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

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

per scopi di legame, utilizzare selectedItems.length come "qualsiasi selezionato" proprietà

+0

Fantastico! Alcune grandi opzioni qui. Grazie. –

+0

È cambiato? –

+0

Sì ... è cambiato? – Reft

0

Oltre agli esempi del Jeremy, è possibile creare un custom setter, per esempio:

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

ho creato un plunker per voi: http://plnkr.co/edit/OTb2RDLZHf5Fy1bVdCB1?p=preview


In questo modo, non eseguirai mai il ciclo per vedere se qualche elemento è cambiato.

+1

Questo è lo stesso dell'esempio "use a collection class", sebbene manchi il decoratore '@computedFrom ('_ isSelected') sulla funzione getter isSelected che significa cheSelected sarà sporco. –

+0

Grazie per il tuo aiuto! –

0

Penso che si possa anche sfruttare EventAggregator come mostrato here. In questo modo non è necessario eseguire il controllo sporco tutto il tempo e invece gestire l'evento di selezione degli oggetti nella propria VM e pubblicare gli eventdata; l'utente dall'altra parte ascolterà lo stesso ed eseguirà la ginnastica necessaria.

Tuttavia, non l'ho mai usato, quindi non sono sicuro dei dettagli più profondi. Ma dalla documentazione sembra abbastanza facile.

0

Jeremy mi ha fatto pensare a questo in this bug. Quindi sembra che tu possa ottenere il binding aggiornandolo tramite un comportamento personalizzato. Spero che Jeremy possa confermare che non sto facendo nulla di troppo stupido qui.

usati in questo modo:

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

Esso sovrascrive lo standard osservare il comportamento e osserva sulla matrice e la proprietà si definisce su ciascuna voce. Probabilmente può essere migliorato per essere più generico ...

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