3

I'm trying to understand the value change handler as described in the documentation Detecting Data Changes with Change Handlers.

I am confused by the event handler when a component attribute is bound to a parent object.

For example i would expect the following component change handler event to always have the value/oldValue as a string.

<aura:component description="testCmp">
    <aura:attribute name="value" type="string"/>
    <aura:handler name="change" value="{!v.value}" action="{!c.handleChange}"/>

    <aura:attribute name="valueHistory" type="String"/>

    Current Value: {!v.value}
    History:
    <pre>
        {!v.valueHistory}
    </pre>
</aura:component>

with the following controller:-

({
    handleChange: function(component, event, helper) {

        var params = event.getParams();
        var oldValue = params.oldValue;
        var value = params.value;

        var valueHistory = component.get('v.valueHistory') || '';
        valueHistory += 'New Value: ' + JSON.stringify(value) + '\n';

        component.set('v.valueHistory', valueHistory);

    }
})

This component by itself works as expected with string history printing as:-

New Value: "Hello 0"
New Value: "Hello 1"
New Value: "Hello 2"
New Value: "Hello 3"
New Value: "Hello 4"

However if i bind the value attribute to a parent object, then the change handler seems to take on the parent object when if fires. ie

Parent Component:-

<aura:application access="GLOBAL" >

    <aura:attribute name="valueObj" type="object" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <button onclick="{!c.incrementMessage}">Change Message (parent)</button>

    <c:testCmp value="{!v.valueObj.message}"/>


</aura:application>

Parent controller:-

({
    doInit: function(component, event, helper) {
        var valueObj = {
            message: 'Hello ' + 0,
            count: 0
        };

        component.set('v.valueObj', valueObj);
    },

    incrementMessage: function(component, event, helper) {

        var valueObj = component.get('v.valueObj');
        valueObj.count++;
        valueObj.message = 'Hello ' + valueObj.count;


        component.set('v.valueObj', valueObj);
    }
})

In this case the original change handler now has an object value:-

New Value: {"message":"Hello 0","count":0}
New Value: {"message":"Hello 1","count":1}
New Value: {"message":"Hello 2","count":2}

There seems to be no way to detect this difference in behaviour and i've had to resort to using component.get('v.value') in the change handler instead of the event value.

Am i missing something here or is this a bug?

2 Answers 2

1

Are you trying to avoid the changes if happening from parent component? Means don't want to fire the change event if value is changed in parent component If so then can you try this expression:

<c:testCmp value="{#v.valueObj.message}"/>

Basically using "#" in attribute binding avoid the creation of standard framework change event handlers. So if the value is getting updated at parent level it will not affect the child attribute.

3
  • Thanks Jai, in this case I do want to know about the change and do some action based on the old and new values. At the moment that doesn't seem possible when the framework is behaving this way.
    – dmdaniel
    Commented Sep 2, 2017 at 9:11
  • Try application event then. It should work. Commented Sep 3, 2017 at 21:29
  • 1
    I was assuming that the change events would work, rather than having to construct my own event to handle passing data between component. This should work, i just don't understand why it doesn't.
    – dmdaniel
    Commented Sep 15, 2017 at 12:51
0

This is the most ridiculous of bugs. I have been fighting this for a while and am at a loss that Salesforce ever let this into production and worse, haven't addressed it yet.

Here's my fix. Instead of using the values sent in the event parameters, just get the new value by accessing the attribute directly

component.get("v.value");

If you need to get the previous value, you can prepare for it on your previous change handling callback or when the component initializes. Copy the current value to some other attribute - then when you get your next change event you can access that attribute to get the previous value.

// on init
component.set("v.prevValue", "");


// your change handler
({
    handleChange: function(component, event, helper) {

        var oldValue = component.get("v.prevValue");
        var value = component.get("v.value");

        var valueHistory = component.get('v.valueHistory') || '';
        valueHistory += 'New Value: ' + JSON.stringify(value) + '\n';

        component.set('v.valueHistory', valueHistory);

        // update prevValue in preperation for the next change event
        component.set("v.preValue", value);
    }
})

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .