2017-02-15 16 views
1

我有一個觀點,即允許用戶查看項目列表,然後選擇一個項目這反過來又使一些按鈕,就像這樣:使用Aurelia repeat.for時,如何從列表中啓用多個選擇?

視圖模型

export class ListViewCustomElement{ 
    @bindable rows= [] 

    selectedRow= null 

    deleteRow() { 
    let event = new CustomEvent('delete-row',{ 
     item: selectedItem, 
     bubbles: true 
    } 

    this.element.dispatchEvent(event) 
} 

視圖

<!-- delete button enabled when user selected --> 
<i class="button fa fa-times" if.bind="selectedRow" click.delegate="deleteRow()"></i> 

<div repeat.for="row of rows" click.delegate="selectedRow = row"> 
    <i class="fa ${selectedRow == row ? 'fa-check-circle' : 'fa-check-circle-o'}"></i> 
    ${row.item1} ${row.item2} 
</div> 

parentview

<list-view rows.bind="users" delete-item.delegate="deleteUser($event)"></list-view> 

現在我需要允許多個用戶被選中。如果該用戶已被選中,我仍然需要每行的圖標來顯示選中的內容。此外,我的deleteRow()函數將需要傳遞事件中的selectedRows列表,而不僅僅是單個用戶。

最簡單的事情是實際添加一個select屬性到每個行元素,但我不能這樣做,因爲它會弄髒我的數據對象。

未遂1

視圖模型

//added to the above vm 
selectedRows = [] 

selectClick(indexRef){ 
    let index = this.selectedRows.indexOf(indexRef) 
    if(index < 0){ 
     this.selectedRows.push(indexRef) 
    } 
    else{ 
     this.selectedRows.splice(index,1) 
    } 
} 

視圖

<!--Changed to call new function --> 
<div ... click.delegate="selectClick($index)"> 
    <i class="fa ${selectedRows.indexOf($index) < 0 ? 'fa-check-circle-o' : 'fa-circle-check'}"></i> 

除了在視圖中的圖標,這一切工作 - 在沒有按的indexOf」由於項目是廣告,因此無法評估DED和selectedRows

未遂2

視圖

<div ref="rowItems"> 
    <div ref="rowItems[$index].unselected" repeat.for="row of rows" 
     click.delegate="rowItems[$index].unselected = !rowItems[$index].unselected"> 
     <i class="fa ${rowItems[$index].unselected ? 'fa-check-circle-o' : 'fa-check-circle'}"></i> 

但是去掉,這種做法讓人難以啓用/禁用刪除按鈕,也導致一個問題,當parentview實際刪除選定的記錄,因爲視圖更新但rowItems屬性的未選標誌未得到重置。

關於完成我所追求的最佳方式的任何想法?

+0

爲什麼不用'isSelected'來「弄髒」數據呢?是一個*選擇的*項目的狀態而不是真實的數據? –

+0

不,isSelected不是數據 - 它是UI的關注點。當數據對象返回到服務器時,我們不希望存儲在數據庫中的UI狀態(尤其是選擇的那樣會改變用戶到用戶等)。另一個問題是這是一個可用於不同數據對象的可重用自定義元素。不保證不會有數據對象具有可能與UI無關的自己的isSelected屬性。 – RHarris

回答

0

因此,這裏是如何我終於解決了這一點。

視圖模型

import {BindingEngine} from 'aurelia-binding' 
@inject(Element,BindingEngine) 

... 

selectedItems = {} 
selectedItemsCount = 0 //used to enable/disable buttons 

constructor(element,bindingEngine){ 
    this.element = element 
    this.bindingEngine = bindingEngine 

    this.psub = this.bindingEngine 
     .propertyObserver(this,"rows") 
     .subscribe((data) => { 
      if(this.asub) this.asub.dispose(); 

      if(!Array.isArray(data)) return; 

      this.asub = this.bindingEngine 
       .collectionObserver(this.rows) 
       .subscribe(changes => { 
        if(changes[0].removed.length){ 
         for(let item in this.selectedItems){ 
          if(!this.rows.find(v => {return v.id == item})){ 
          this.selectedItemsCount -= (this.selectedItems[item] ? 1 : 0) 
          delete this.selectedItems[item] 
          } 
         } 
        } 
       }) 
     }) 
}  

selectClick(id){ 
    this.selectedItems[id] = !this.selectedItems[id] 

    let total = 0 
    for(let item in this.selectedItems){ 
     total += this.selectedItems[item] ? 1 : 0 
    } 

    this.selectedItemsCount = total 
} 

deleteRow(item){ 
    let responseItem = null 
    if(!item){ 
     responseItem = [] 
     responseItem = this.rows.filter(v => {return this.selectedItems[v.id]}) 
    } 
    else responseItem = item; 

    let event = new CustomEvent('delete-row',{ 
     detail: {item: responseItem, isList: Array.isArray(responseItem)}, 
     bubbles: true 
    }) 

    this.element.dispatchEvent(event) 
} 

首先,實際上是選擇一個代碼類似於peinearydevelopment建議是什麼 - 所以感謝讓我在正確的方向前進。但是,當選擇或取消選擇項目時,我需要啓用/禁用「全局」刪除按鈕(列表外部)。這是selectClick函數中selectedItemsCount更新的目的。

但是,更大的問題是,如果我選擇了5個項目並單擊刪除,則父視圖(擁有行列表)負責決定是否應刪除行並實際刪除它們。發生這種情況時,我的行已更新,但我的selectedItemsCount不是。如果項目被添加到列表中,情況也是如此。

所以,這就是構造函數中屬性和集合觀察者代碼的用途。它確保隨時添加項目或從項目中刪除項目,清理selectedItems列表並調整selectedItemsCount

注意:在很多情況下propertyObserver代碼可能不是必需的,但在我的情況下,行並不總是在調用構造函數時加載 - 也不是在視圖被附加時 - 數據加載器不會將新數據推送到現有數組中,而是將其替換掉。替換殺死了我的collectionObserver所連接的數組,因此有必要注意並附加一個新的collectionObserver。

視圖

<!-- delete button enabled when user selected --> 
<i class="button fa fa-times" if.bind="selectedItemsCount" click.delegate="deleteRow()"></i> 

<div repeat.for="row of rows"> 
    <i class="fa ${selectedItems[row.id] ? 'fa-check-circle' : 'fa-check-circle-o'}"></i> 
${row.item1} ${row.item2} 
</div> 

我敢肯定,奧裏利亞隊伍有處理這一切更加優雅的方式;但希望這可以幫助其他人。

再次感謝peinearydevelopment和Eliran的意見。我同意你們兩個加入一個isSelected標誌到每一行將是一個更簡單的解決方案太多

0

也許我失去了從你的問題的東西,但我想你會只是希望這樣的事情:

<div repeat.for="row of rows" click.delegate="row.isSelected = !row.isSelected"> 
    <i class="fa ${row.isSelected ? 'fa-check-circle' : 'fa-check-circle-o'}"></i> 
    ${row.item1} ${row.item2} 
</div> 

如果行對象不具有isSelected屬性,那麼它最初將解決到undefined這是錯誤的。一旦用戶點擊一次,!將強制它爲真的布爾值,進一步的點擊將切換該布爾值。

我假設你會想在你班上稍後的時間做些選擇。然後,你可以使用JavaScript的數組過濾函數來獲取所有選定的項目:

var selectedRows = rows.filter(function(row) { return row.isSelected; }); 

UPDATE: 對不起,我以前看錯你的約束。但我同意Eliran Malka。我認爲在這些場景中的一般方法是,將服務中的數據接收到它們的視圖模型中,然後將數據操縱到視圖中綁定到視圖的特定對象。如果這些對象中的某些數據需要保存,那麼會有一些東西將這些視圖對象轉換回服務對象。因此isSelected屬性不會讓你的數據變得「髒」。

也就是說,根據您需要跟蹤這些更改的原因以及爲什麼需要跟蹤這些更改,有幾種不同的選項,其範圍很複雜。

我可以用你提供的信息看到的最簡單的方法與第一種方法類似。請讓我知道如果這還不夠,我們可以嘗試另一種選擇。

視圖

<div repeat.for="row of rows" click.delegate="selectedIndices[$index] = !selectedIndices[$index]"> 
    <i class="fa ${${selectedIndices[$index] ? 'fa-check-circle' : 'fa-check-circle-o'}"></i> 
    ${row.item1} ${row.item2} 
</div> 

視圖模型

selectedIndices = {} 
+0

是的,那將是簡單的方法。但是,我無法將isSelected添加到我的數據對象。按照你的建議,通過添加一個新屬性實際上改變了我的數據對象的結構。 – RHarris

+0

正確,對不起,也許我錯過了它,但是我沒有看到你的問題在哪裏,你說過你不能這麼做。 – peinearydevelopment

+0

NP,我已經粗體顯示了它,以便讓這個限制更加明顯 - 它在那裏,在所有其他文本中很難找到。 – RHarris