Reputation: 14416
I have bound an isSelected(item) function to my button which will toggle the class to show if it is selected or not, however this function will not update on changes.
I can not use a computed property as it takes a parameter.
Is there any way to make this work with a bound function and getting it to update ideally with a @computedFrom decorator or the like?
Example here:
https://codesandbox.io/s/aurelia-typescript-sandbox-8oksr?fontsize=14
You will notice that person 2 is bound correctly via the function, but clicking the other items will not update the UI.
-
The reason I want to do this is a result of a somewhat complex source array. Rather than the persons array I have given as an example here. My real domain is closer to a list of boxes with items that can have other boxes (potentially infinitely) where items can be selected at any level.
Upvotes: 2
Views: 1279
Reputation: 514
multi select utilizing signaler
import "./app.css";
import {BindingSignaler} from 'aurelia-templating-resources';
import { inject } from "aurelia-framework";
@inject(BindingSignaler)
export class App {
message = "Hello World!";
message2 = "Hello World2!";
people = [{ name: "Person 1" }, { name: "Person 2" }, { name: "Person 3" }];
selections = [];
constructor(private signaler: BindingSignaler) {
this.selections.push(this.people[1]);
}
selectPerson(person) {
this.selections.push(person);
this.signaler.signal('select-signal')
}
color(person) {
return this.selections.includes(person) ? 'green' : 'red';
}
}
<template>
<h1>People</h1>
<div repeat.for="person of selections">
${person.name}
</div>
<button
class="btn-gradient mini ${color(person) & signal:'select-signal' }"
click.delegate="selectPerson(person)"
repeat.for="person of people"
>
${person.name}
</button>
</template>
single select
add selected
into class,
assign person
in it on change
and then use selected === person
as condition
import "./app.css";
export class App {
message = "Hello World!";
message2 = "Hello World2!";
people = [{ name: "Person 1" }, { name: "Person 2" }, { name: "Person 3" }];
selections = [];
// add selected
selected = null;
constructor() {
// use selectPerson
this.selectPerson(this.people[1]);
}
selectPerson(person) {
this.selections.push(person);
// assign person to selected
this.selected = person;
}
}
<template>
<h1>People</h1>
<div repeat.for="person of selections">
${person.name}
</div>
<!-- use `selected === person` as condition -->
<button
class="btn-gradient mini ${selected === person ? 'green': 'red'}"
click.delegate="selectPerson(person)"
repeat.for="person of people"
>
${person.name}
</button>
</template>
Upvotes: 0
Reputation: 11187
Update 2
So I ran across this issue on github. One of the answers indicated that passing an observed item into a method automatically makes the method observable. Currently, you're only passing person
into isSelected()
. But, person isn't being changed. I think you can accomplish what you're looking for by changing your isSelected()
method like so (notice the change in the call to isSelected in the class binding of the button):
vm.ts
public isSelected(person, length){
return this.selections.find(item => item.name === person.name);
}
view.html
<button
class="btn-gradient mini ${isSelected(person, selections.length) ? 'green': 'red'}"
click.delegate="selectPerson(person)"
repeat.for="person of people">
${person.name}
</button>
Example: https://codesandbox.io/s/aurelia-typescript-sandbox-oelt7?fontsize=14
Original Post
I'm struggling with the same issue with trying to implement an isSelected()
method for controlling a selected indicator class. I've looked into @computedFrom
.
I may be wrong on this, but from what I've seen @computedFrom
can only be used with un-paramaterized getters.
@computedFrom('firstName', 'lastName')
get fullName() { return `${firstName} ${lastName}`}
So the problem with what we're wanting to do is that we need to pass in an index or an item to our method -- which breaks our ability to use @computedFrom
.
An alternative, which I don't really like ... but it does work, is to add an isSelected
property to each of your person objects. Then your code would look something like this:
vm.ts
selectPerson(person){
person.isSelected = !person.isSelected; //De-selects if already selected
}
view.html
<button
class="btn-gradient mini ${person.isSelected ? 'green': 'red'}"
click.delegate="selectPerson(person)"
repeat.for="person of people">${person.name}</button>
(or, as was recently suggested to me, wrap your person object in a wrapper class)
public class SelectableWrapper {
constructor(public person : Person, public isSelected : boolean){}
}
Update 1
To address the issue of displaying the list of selected items (as well as "coloring" the selected items), you could do the following (in addition to what I've already shown):
vm.ts
//Remove selections property and add it as a getter
get selections(){
return this.people.filter(p => p.isSelected);
}
view.html
<div repeat.for = "person of selections">
${person.name}
</div>
Example here: https://codesandbox.io/s/aurelia-typescript-sandbox-u92nk?fontsize=14
Upvotes: 1