jokul
jokul

Reputation: 1339

Observing Array Properties in Aurelia

I have a property on my class:

class Control {
    @bindable households;

    get people() {
        return households
            .map(household => househould.people)
            .reduce((g1, g2) => g1.concat(g2), []);
    }
}

Which I use to compute a collection of all people[] within all households which is then rendered here:

<ul>
    <li repeat.for="person of people">
        ${person.firstName} ${person.lastName} - ${person.phone}
    </li>
</ul>

I need the list to update whenever people are added to a household, OR if any of the rendered properties, firstName, lastName, phone, for any element in the computed collection is updated. How can I do this in Aurelia? If I use @computedFrom() it will not detect changes to elements of the array, and since the list of people in all households is dynamic, I cannot just create an observer for each element without creating a system for managing when observers should be subscribed / unsubscribed.

Upvotes: 0

Views: 628

Answers (3)

Saeed Ganji
Saeed Ganji

Reputation: 307

You must avoid dirty-checking as far as possible, signals are the perfect option for your scenario. Just bear in mind that if you want to use computedFrom on an array you can do so by watching its length property for instance rather than dirtyChecking, sth like the following @computedFrom("myArray.length")

Upvotes: 0

Matthew James Davis
Matthew James Davis

Reputation: 12295

Use Dirty Checking

Leave off @computedFrom() and you'll achieve the desired behavior.

export class App {
  @bindable households;
  get people() {
    const households = this.households || []; // Make sure househoulds is defined.
    return households.reduce((people, household) => people.concat(household.people), []);
  }
}

https://gist.run/?id=040775f06aba5e955afd362ee60863aa

Upvotes: 2

jokul
jokul

Reputation: 1339

Right as I was about to give up on being able to Google for a solution, Aurelia Signaling came to the rescue. This code ended up working for me:

<ul>
    <li repeat.for="person of people">
        <!-- May work without this rendering method,
            this is just closer to what my actual code is doing. -->
        ${renderPersonInfo(person) & signal: 'example-signal'}
    </li>
</ul>

And the class:

import {BindingSignaler} from 'aurelia-templating-resources';

@inject(BindingSignaler)
class Control {
    @bindable households;

    constructor(bindingSignaler) {
        this.bindingSignaler = bindingSignaler;
        //Obviously, you can have this trigger off any event
        setInterval(() => this.bindingSignaler.signal('example-signal'), 1000);
    }

    get people() {
        return households
            .map(household => househould.people)
            .reduce((g1, g2) => g1.concat(g2), []);
    }
}

Upvotes: 1

Related Questions