2017-02-15 6 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() необходимо будет передать список выбранных строк в событии, а не только одного пользователя.

Проще всего было бы добавить свойство 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 Безразлично» t получает оценку, поскольку элементы являются объявлением 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 - это не данные - его проблема с пользовательским интерфейсом. когда объект данных возвращается на сервер, нам не нужно состояние пользовательского интерфейса (особенно что-то вроде выбранного, поскольку это может изменить пользователя для пользователя и т. д.), хранящегося в БД. Другая проблема заключается в том, что это повторно используемый пользовательский элемент, который работает на разных объектах данных. Нет никакой гарантии, что не будет объекта данных, у которого есть свое свойство isSelected, которое не может быть связано с пользовательским интерфейсом. – RHarris

ответ

0

Так вот, как я, наконец, решил это.

ViewModel

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 предложил - так что спасибо за получение меня движется в правильном направлении. Тем не менее, мне нужно, чтобы «глобальная» кнопка удаления (вне списка) была включена/отключена, когда элементы были выбраны или отменены. Это назначение выбранного значения ItemsCount в функции selectClick.

Однако большая проблема заключается в том, что если я выбрал 5 элементов и щелкнул delete, родительский вид (которому принадлежит список строк) отвечает за решение о том, следует ли удалять строки и фактически удалять их. Когда это произошло, мои строки были обновлены, но мой 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> 

Я уверен, что команда Aurelia будет иметь гораздо более элегантный способ обработки все это; но, надеюсь, это помогает и другому.

Опять же, благодаря 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, который является ложным. Как только пользователь нажмет на него один раз, ! будет принуждать его к значению bool true, а дальнейшие клики будут переключать это логическое значение.

Предполагаете, что вам захочется что-то сделать с выборами в более поздней точке вашего класса.Затем вы можете использовать функцию фильтра массив 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> 

ViewModel

selectedIndices = {} 
+0

Да, это был бы простой способ. Однако я не могу добавить isSelected в мои объекты данных. Выполнение того, что вы предложили, фактически изменяет структуру моего объекта данных, добавив новое свойство. – RHarris

+0

Правильно, извините, возможно, я пропустил это, но я не видел, где в вашем вопросе вы заявили, что не можете этого сделать. – peinearydevelopment

+0

NP, я выделил его, чтобы помочь сделать это ограничение немного более заметным - его там, просто трудно найти во всем другом тексте. – RHarris

 Смежные вопросы

  • Нет связанных вопросов^_^