[SalesForce] bind a ui:inputCheckBox value to the inverse of an object field’s value

I came across this occurrence while developing a lightning component. I'm trying to bind a checkbox value to the inverse of the value of the NetworkMember.PreferencesDisableAllFeedsEmail field. But the value does not change when the checkbox is clicked/changed. Here's a code sample:

<aura:attribute name='member' type='NetworkMember'/>

<div class="slds-order--1 slds-medium-order--1 slds-large-order--1">
    <ui:inputCheckbox value='{!!v.member.PreferencesDisableAllFeedsEmail}' change='{!c.allPrefDisabled}'/>
    <label class="slds-form-element__label">Recieve Emails</label>
</div>

When I do this (note the ! in the value attr), the field's value never changes from it's original state, despite changing it in the UI. However, if I remove the ! like so:

<aura:attribute name='member' type='NetworkMember'/>

<div class="slds-order--1 slds-medium-order--1 slds-large-order--1">
    <ui:inputCheckbox value='{!v.member.PreferencesDisableAllFeedsEmail}' change='{!c.allPrefDisabled}'/>
    <label class="slds-form-element__label">Recieve Emails</label>
</div>

The value is updated accordingly. Of course, I could change the label to Disable Emails as a workaround but I can't imagine that this is expected behavior. Has anyone observed this before and is this a platform bug, or is there some reasoning for this?

Any insight is appreciated. Thanks!

Edit: I also tried using the lightning function not listed here: docs as opposed to the ! and observed the same behavior.

Best Answer

When you add the negation to the value property it turns the expression to a FunctionCallValue. Since it is no longer directly mapped to an attribute (as it is with the single !), clicking the checkbox no longer updates the original attribute. Imagine if instead of a simple negation there was a more complicated expression like value="{!v.myAttr.foo == 'meep'}". The framework doesn't know what to set back to this expression when the checkbox is clicked, so it ignores it.

In fact, if you go to Setup --> Lightning Components and check the "Enable Debug Mode" checkbox, you'll see a similar error in the console:

aura_dev.js:3977 AttributeSet.set(): unable to override the value for 'value=function (cmp, fn) { return !(cmp.get("v.myAttr.foo")); }'. FunctionCallValues declared in markup are constant.

There's probably a better way to handle this, but one workaround is to create a new attribute that is bound to the ui:inputCheckbox value and then in the change handler update v.member.PreferencesDisableAllFeedsEmail. Here's a simplified example:

Markup:

<aura:attribute name="checkboxValue" type="Boolean" default="false"/>
<ui:inputCheckbox value='{!v.checkboxValue}' change='{!c.changed}'/>

Controller:

changed: function(cmp) {
    var val = cmp.get("v.checkboxValue");
    cmp.set("v.member.PreferencesDisableAllFeedsEmail", !val);
}

In your case you'd probably want to set the initial value of checkboxValue to the negation of member.PreferencesDisableAllFeedsEmail in the init handler (or similar).

Related Topic