Property change subscription with Aurelia
Asked Answered
N

4

35

I have a property on my viewmodel which I want to listen to and trigger events based on its value, like this:

class viewModel {
  constructor() {
    this.value = '0';
    let val = 2;
    subscribe(this.value, callbackForValue);
    subscribe(val, callbackForVal);
  }
}

Is this a feature of Aurelia? If so, how would I go about setting up such a subscription?

Nugatory answered 9/2, 2015 at 20:59 Comment(5)
I'm pretty sure there is an Aurelia abstraction of this?Nugatory
Actually you were the first to show me about Aurelia, but I knew about Object.oberve() already and when I went to the Aurelia website that showed up. Yea that's why I didn't answer just commented sorry if that didn't help.Microelectronics
@edwin Thanks for your work :) Keep it up.Nugatory
You can actually use the @bindable annotation on the property you want to observe and it will call the [property name]Changed function on the view model class when the property value changes. Probably not what @bindable was meant for, but it works.Treat
@DecadeMoon, probably @observable annotation was designed for purpose you talking about.Albertalberta
H
54

In some plugins I've been using DI to get the ObserverLocator instance from the container:

import {inject} from 'aurelia-dependency-injection';  // or from 'aurelia-framework'
import {ObserverLocator} from 'aurelia-binding';      // or from 'aurelia-framework'

@inject(ObserverLocator)
export class Foo {
    constructor(observerLocator) {
        this.observerLocator = observerLocator;
    }
    ...
}

You can then do something like this:

var subscription = this.observerLocator
    .getObserver(myObj, 'myPropertyName')
    .subscribe(myCallback);

When you're ready to dispose of the subscription, invoke it:

subscription();

I think this is all subject to change but it's something you could use right now if you needed to.

More info here

October 2015 update

The ObserverLocator is Aurelia's internal "bare metal" API. There's now a public API for the binding engine that could be used:

import {inject} from 'aurelia-dependency-injection';  // or from 'aurelia-framework'
import {BindingEngine} from 'aurelia-binding';        // or from 'aurelia-framework'

@inject(BindingEngine)
export class ViewModel {
  constructor(bindingEngine) {
    this.obj = { foo: 'bar' };

    // subscribe
    let subscription = bindingEngine.propertyObserver(this.obj, 'foo')
      .subscribe((newValue, oldValue) => console.log(newValue));

    // unsubscribe
    subscription.dispose();
  }
}
Hookworm answered 10/2, 2015 at 17:0 Comment(6)
Could you use the Event Aggregator feature of Aurelia?Fantastically
@ChiRow Please don't abuse the EventAggregator! It can be used in this case, but more often than not if you're thinking about subscribers you're thinking about tightly coupled behavior, and the EventAggregator is for loosely coupled behaviors.Nugatory
@jeremy-danyow well done, lets not forget to update this when the api does change :)Nugatory
@jeremy-danyow what is the difference between propertyObserver and expressionObserver?Thibodeaux
propertyObserver observes a specific property (eg firstName) and notifies you when the property changes. expressionObserver observes an entire expression (eg foo.bar[x][baz].hello() * test / something) and notifies you when the result of the expression changes.Hookworm
In case others find this answer I'd like to point out that there is a subtle difference with @JeremyDanyow answer and others. He did answer what the OP was after, "how do I observe the property changes of a dependency." The other answers provided answer the following, "how do I observe property changes of myself."Lunular
H
12

The observable attribute has less of an overhead to binding according to I kill nerds.

import {observable} from "aurelia-framework";

export class Example {

    @observable
    public description: string;

    private descriptionChanged(newValue: string, oldValue: string): void {

    }
}
Hydrops answered 2/9, 2016 at 11:43 Comment(0)
W
10

listen to and trigger events based on its value

A snippet from code using TypeScript, hopefully that will get you an idea:

import {bindingMode} from "aurelia-binding";

export class Example{

    @bindable
    public description: string;

    private descriptionChanged(newValue: string, oldValue: string): void {
        console.log(newValue, oldValue);
    }
}

Method name should follow convention `${propertyName}Changed`


EDIT: That's exactly what Decade Moon suggested in the comment above: Property change subscription with Aurelia

Watchful answered 12/7, 2016 at 10:28 Comment(2)
I like this solution best. It uses significantly less code to accomplish the same task.Homestead
It works because description is a string. The metod above doen't work with objects and arrays, like the original question asked for. As far as I know.Endometriosis
S
2

The @observable decorator works fine for this scenario.

You could use the BindingEngine to watch a collection or control when to subscribe/unsubscribe

Siegfried answered 5/1, 2017 at 14:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.