Search code examples
javascriptaurelia

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 :(


Solution

  • 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.