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?

Jeremy Danyow
  • 26,470
  • 12
  • 87
  • 133
Matthew James Davis
  • 12,134
  • 7
  • 61
  • 90
  • I'm pretty sure there is an Aurelia abstraction of this? – Matthew James Davis Feb 10 '15 at 14:30
  • 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. – Edwin Reynoso Feb 10 '15 at 17:45
  • 2
    @edwin Thanks for your work :) Keep it up. – Matthew James Davis Feb 11 '15 at 01:27
  • 5
    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. – Decade Moon Jun 25 '15 at 01:17
  • @DecadeMoon, probably `@observable` annotation was designed for purpose you talking about. – Fabio Oct 05 '19 at 22:36

4 Answers4

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();
  }
}
Jeremy Danyow
  • 26,470
  • 12
  • 87
  • 133
  • Could you use the Event Aggregator feature of Aurelia? – Chi Row Feb 10 '15 at 18:22
  • 3
    @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. – Matthew James Davis Feb 11 '15 at 01:26
  • 3
    @jeremy-danyow well done, lets not forget to update this when the api does change :) – Matthew James Davis Feb 11 '15 at 01:27
  • @jeremy-danyow what is the difference between propertyObserver and expressionObserver? – smiggleworth Aug 25 '16 at 00:05
  • `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. – Jeremy Danyow Aug 25 '16 at 00:19
  • 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." – blandau Nov 01 '17 at 15:57
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 {

    }
}
sharky101
  • 812
  • 5
  • 11
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

Community
  • 1
  • 1
Mars Robertson
  • 12,673
  • 11
  • 68
  • 89
2

The @observable decorator works fine for this scenario.

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

Andrew
  • 5,395
  • 1
  • 27
  • 47