ghiscoding
ghiscoding

Reputation: 13194

How to know the valueChanged origin in Aurelia?

I created a custom element in Aurelia and I also have the valueChanged, however I need to do a certain action only when the value is changed outside of the custom element. Since the signature is valueChanged(newValue, oldValue), how would I know when the value gets changed from the ViewModel and not from the custom element itself? Is that doable somehow with an observer or observable?

I actually got kind of a working sample, I saw that there's also an __array_observer__ property when the value is changed from the ViewModel, and it works but it's probably not ideal. So I got this piece of code which kinda works

valueChanged(newValue, oldValue) {
  if (newValue !== oldValue && newValue.__array_observer__) {
    // value got changed outside of this custom element
  }
}

This is probably not ideal though, or is it? Any other suggestion in knowing where the value got changed outside of the custom element?

EDIT

As much as possible, I'm looking for a solution that will still have access to the custom element. Even if I want to get triggered by an external value change call, I still need to call an internal function of the same custom element.

EDIT #2

To give a little more description of my issue, I need to know when the value got changed from the outside because this will trigger an action that will re-affect the value. Without knowing if the change was from the outside of the custom element, I fall in a recursive call with no way to stop it. What I'm looking for is similar to what used to be the caller and the callee but this was removed with ES5 and Strict Mode, however this would have been very useful.

Still looking for an answer :(

Upvotes: 2

Views: 1239

Answers (2)

Maxim Balaganskiy
Maxim Balaganskiy

Reputation: 1574

As discussed in gitter, you can use a suppress flag

value: number;
suppressValueChanged: boolean;
valueChanged(){
  if(this.suppressValueChanged){
    this.suppressValueChanged = false;
    this.logger.debug("the value has been changed from inside the element");
    return;
  }
  this.logger.debug("the value has been changed from outside the element");
  // here comes the code to run when the value is changed outside
}

internalSetValue(value: number){
  this.suppressValueChanged = true;
  this.value = value;
}

The reason I reset the flag in the changed method is that depending on the circumstances valueChanged can be called by Aurelia asynchronously so you cannot just do the following

  this.suppressValueChanged = true;
  this.value = 123;
  this.suppressValueChanged = false;

Sometimes, using a task will work

  this.taskQueue.queueTask(() => {
    this.suppressValueChanged = true;
    this.value = 123;
    this.suppressValueChanged = false;
  });

It really depends where exactly in Aurelia code you are changing the value. I've found that the first option gives the most consistent result.

Upvotes: 1

Fabio
Fabio

Reputation: 11990

You could use a CustomBindingBehavior to intercept the updateTarget event. For instance:

export class InterceptBindingBehavior {
  bind(binding, scope, interceptor) {
    binding['originalUpdateTarget'] = binding['updateTarget'];
    binding.updateTarget = val => { 
      alert('property was changed outside of the element');
      //do something here
      binding['originalUpdateTarget'](val);   
    }
  }

  unbind(binding, scope) {
    binding.updateTarget = binding['originalUpdateTarget'];
    binding['originalUpdateTarget'] = null;
  }
}

Usage:

<template>
  <require from="./intercept-binding-behavior"></require>

  <some-element value.bind="message & intercept"></some-element>
</template>

Runnable example: https://gist.run/?id=bcd7d39ed94856caf586f224f89fd1ff

I haven't tested this in many cases and I'm not sure if it's best way.

If you want to do the opposite (intercept when the property is changed from the element instead of the VM) just replace updateTarget for updateSource.

More info about CustomBindingBehaviors http://aurelia.io/hub.html#/doc/article/aurelia/binding/latest/binding-binding-behaviors/8

Hope this helps!

Upvotes: 2

Related Questions