Property observer in @noView class

I want to use the BindingEngine property observer within a regular JavaScript class, not a View Model as in https://aurelia.io/docs/binding/binding-engine.

My class is decorated with @noView (https://github.com/ome/omero-iviewer/blob/eb9749d6b6f5c7a0878c84d21c164431b2cd4a8e/src/model/regions_info.js#L34) and it seems that when I try to get hold of a BindingEngine via a static inject() as in that example, or using the @inject decorator as in https://github.com/ome/omero-iviewer/blob/master/src/regions/regions-list.js#L41, any additional parameters I add to the constructor (https://github.com/ome/omero-iviewer/blob/master/src/model/regions_info.js#L200) are giving me undefined.

I could set up the property observation in a view model, such as regions-list.js above, but it seems that it this should be the responsibility of the underlying model object.

Many thanks,
Will.

1 Like

Hi @will-moore.

Have you tried this: https://aurelia.io/docs/binding/observable-properties#observable-properties?

It seems to me that you want some observable properties. If that’s all what you need, then you can simply use the @observable decorator on your property. That should work, irrespective of whether your class is a view model, or not. And you don’t have to bother about the BindingEngine. That is useful where you don’t have access to source code of the class where you want to observe property, or don’t want to put Aurelia dependency for whatever reason.

1 Like

Thanks for your help.
So, the @observable is just for observing changing properties of a class?
My class has a property called data which is a Map where the values are regular JS objects (created when I dynamically load some JSON data). I want to observe the ‘show’ property of every object in this Map. The UI is a bit like a hierarchy browser - loading ‘folders’ then listening for each one to be expanded -> then loading child objects.

Currently I’m setting this up using the bindingEngine like this in the model view:

    // for each object in the Map, observe the 'show' property.
    this.regions_info.data.forEach((roi, roi_id) => {
        this.observers.push(this.bindingEngine.propertyObserver(roi, 'show').subscribe(
            (newValue, oldValue) => {
                this.handleRoiShow(newValue, oldValue, roi_id);
            }));
    });

Using the subscribe() closure allows me to pass an additional roi_id parameter to the handler.
This is working but I would prefer it if the regions_info class itself was able to setup these observers.

Many thanks,
Will

1 Like

Now I think that I have understood the issue. I think instead of using a raw object literal when setting the value of a region id in the map, it might be easier to use a class like this.

class Region {
  @observable public show: boolean = false;
  public deleted: number = 0,

  public constructor(
    public name: string,
    public shapes: Shapes, // assuming the Shapes type exists
  ){}

  private showChanged(){
    // handle the change event here;
    // or publish the `{name}:show:changed` event from here to notify the actual handler(s).
  }
}

Then you can use the following to set values in the map.

this.data.set(roiId, new Region(name, shapes));

I have used TS syntax, but I think you can transfer that to JS as well. Does this approach help you?

3 Likes

Thanks for that. I implemented it in https://github.com/ome/omero-iviewer/pull/351/commits/b15f7d314131dd7d018ffcaee047f0a1b274a6b9

Cheers,
Will.

3 Likes