Aurelia custom Binding Behavior to observe ALL properties of an object

Viewed 347

I would like to create a custom Binding Behavior that allows me to detect ANY changes to the properties of an object, like this:

<my-form model.bind="myObject & objectObserver:myObjChanged()"></my-form>

I know that I can use Aurelia's binding engine to create a property observer, and perhaps I can build this into a custom Binding Behavior to detect the properties of the object and create property observers for each one. But I can't make sense of the binding object that is given to me inside the custom Binding Behavior. Here's my code so far:

import { inject, bindingBehavior, BindingEngine } from 'aurelia-framework';

@bindingBehavior('objectObserver')
@inject(BindingEngine)
export default class ObjectObserverBindingBehavior {
  constructor(bindingEngine) {
    this.bindingEngine = bindingEngine;
  }

  bind(binding, scope, interceptor) {
    console.warn('hello', binding, scope, interceptor);
  }

  unbind(binding, scope) {
    console.warn('observer.unbind()', binding, scope);
  }
}

When the bind happens and the console text is output, I see enter image description here

So I know it's working, but I don't know what the best object is to start watching. I see the bound object inside targetObserver.currentValue. Is that the best property to start watching? Is there another way that utilizes existing functionality of the Aurelia Binding Engine?

1 Answers

I found a solution that is not Aurelia specific, based on the Proxy functionality built into Javascript.

export function onChangeObj(object, onChange) {
  // creates Proxy to detect changes in object properties and call a function
  if (typeof onChange !== 'function' || typeof object !== 'object') {
    throw new Error('onChangeObj: incorrect parameters');
  }
  const handler = {
    set(obj, prop, value) {
      onChange(prop, value);
      return Reflect.set(obj, prop, value);
    },
  };
  return new Proxy(object, handler);
}

In order to use it, just call it like this:

this.myObject = onChangeObj(this.myObject, () => this.myObjChanged());

Effectively, the object is replaced by a wrapper Proxy that calls the provided function every time one of the properties is modified (with the setter).

If anyone finds a solution via Aurelia Binding Behavior, I would still be interested.

Related