Marking selected items in a list

It seems like I’m always display lists where I need to allow the user to select either a single item or multiple items. In each case, items that are selected need to have an icon displayed indicating that they’re selected. I’ve handled this differently for the two cases.

For Single Select I’ve done something like this:

vm

public items : any[] = [ ... ];
public selectedItem : any = null;

public selectItem(item: any){
    if(this.selectedItem == item)
         this.selectedItem = null;
    else
          this.selectedItem = item;
}

view

<tr repeat.for="item of items" click.delegate="selectItem(item)">
     <td><i class="${selectedItem == item ? 'fas fa-check-circle' : 'far fa-circle'}"></i></td>
     <td>${item.name}</td>
</tr>

For Multiple Selections I’ve actually added a selected property to each item in my list.

vm

public items: any[] = [ ... ];

public selectItem(item){
   item.selected = !item.selected;
}

Question

I’m trying to find a way to not have to “corrupt” my objects by adding a selected property when dealing with multiple selections. I can use a selectedItems : any[] array to track my items, but I can’t figure out a way to update the selected indicator in the view.

Has anyone done something like this?

1 Like

Hi,

maybe you could put the list-item in it’s own custom-element. It would show an icon depending on if it is selected or not and your container listens to the (custom-)selected-event, so it can keep track of selected items. That way you would not have to mix your object data with the selection state.
Something like

<list-item repeat.for="item of items" click.trigger="toggleSelection()">

An example for sending custom events can be found in the code to the book “Aurelia in Action” (star-rating).

1 Like

Did you try

     <td><i class="${isSelected(item) ? 'fas fa-check-circle' : 'far fa-circle'}"></i></td>

where

isSelected = (item) => selectedItems.contains(item)

?

1 Like

I would create a generic wrapper class like this:

export class SelectableItem<T> {
  item: T;
  isSelected: boolean;
}
1 Like

Yes I did try this. It would appear that the isSelected(item) method does not get re-evaluated.

1 Like

If you are using a list of checkboxes, then Aurelia gives you an ability to bind to a predefined array, as can be seen from this example https://codesandbox.io/s/aurelia-wysiwyd-template-6ot0y (the same item array, bound to 2 different selection lists)

Thanks @bigopon, but could you show how you would “highlite” selected items — IOW, make them bold or green. That seems to be where I’m having my trouble because there doesn’t seem to be a way to evaluate which items are selected (without the use of an isSelected indicator on the item iteslf).

1 Like

So, I’m not sure how I feel about the “hacky” nature of this solution, but I did run across a post that indicated that passing an observed item into a method automatically causes the method to be observed.

So, I can accomplish what I’m after by doing the following:

vm.ts

public items : any[] = [...]
public selectedItems : any[] = []

public selectItem(item){
    let index = this.selectedItems.indexOf(item);
    if(index >= 0)
        this.selectedItems.splice(index,1);
    else
        this.selectedItems.push(item);
}

public isSelected(item, length){
    return this.selectedItems.some(s => s === item);
}

view.html

<tr repeat.for="item of items" click.delegate="selectItem(item)">
    <td><i class="${isSelected(item, selectedItems.length) ? 'fas fa-check-circle' : 'far fa-circle'}"></i></td>
    <td>${item.name}</td>
</tr>
1 Like