3

I have component which holds Object[] attribute. On init action I am loading data in it and showing that for user interaction. All works well and update on input field gets bonded properly with original attribute.

I then call controller function on button click and performs some validations on each element of Object[] and display some error message. After that if I am updating/correcting the values in input field then that value is not getting bind to original Object[] elements.

Maincomponent.cmp:

<aura:attribute name="milestones" type="Object[]" />
<div aura:id="saveconfirmation"></div>
<aura:iteration items="{!v.milestones}" var="mile">
    <c:PGP_VariationMilestone milestone="{!mile}" />
</aura:iteration>

<ui:button label="Save" press="{!c.saveVariation}" class="slds-button slds-button--brand" aura:id="savemilestonebutton" /> 

Here Object[] is list of wrapper class.

PGP_VariationMilestone.cmp

<aura:component controller="PGPVariationCompClass">
    <aura:attribute name="milestone" type="Object" />

    <div class="slds-form-element" style="width:180px;">
        <ui:inputText label="Milestone Number" value="{!v.milestone.name}" disabled="{!(v.milestone.recordId != '') ? 'true' : 'false'}" class="slds-input" labelClass="slds-form-element__label" change="{!c.updateActionOnChange}" />
    </div>

    <div class="slds-form-element" style="white-space: nowrap;width:130px">
        <ui:inputDate aura:id="dueDate" label="Due Date" value="{!v.milestone.dueDate}" displayDatePicker="true" class="slds-input" labelClass="slds-form-element__label" blur="{!c.dateOnChange}" />
    </div>
</aura:component>

MainComponentController.js

saveVariation: function(component, event, helper) {
    var milestoneArray = component.get('v.milestones') ;
    console.log('------in saveVariation milestone-----' + JSON.stringify(component.get('v.milestones')));

    var isValid;
    var milestonenames = '';
    var deliberablenames = '';
    var i, j;
    // deliverables is a list of wrapper class. each milestoneArray element has list of  deliverables[]

    for (i = 0; i < milestoneArray.length; i++) {
        if (!milestoneArray[i].description ||
            !milestoneArray[i].dueDate ||
            (!milestoneArray[i].mpifund && milestoneArray[i].miletype == 'Payment')) {
            isValid = false;
            milestonenames = milestonenames + milestoneArray[i].name + ', ';
        }

        for (j = 0; j < milestoneArray[i].deliverables.length; j++) {
            console.log('------for loop deliverable data----' + JSON.stringify(milestoneArray[i].deliverables[j]));
            if (!milestoneArray[i].deliverables[j].dueDate ||
                !milestoneArray[i].deliverables[j].startDate ||
                !milestoneArray[i].deliverables[j].name ||
                !milestoneArray[i].deliverables[j].description) {
                isValid = false;
                deliberablenames = deliberablenames + milestoneArray[i].deliverables[j].name + ', ';
            }
        }
    }

    console.log('----isValid----' + isValid);

    if (isValid == true) {
        helper.saveVariationHelper(component);
    } else {
        console.log('------in else of isValid error-----');
        $A.createComponents([
                ["ui:message", {
                    "title": "Error",
                    "severity": "error",
                    "closable": false
                }],
                ["ui:outputText", {
                    "value": 'Please correct the errors for Miletones:- ' + milestonenames + 'and Deliverables:- ' + deliberablenames,
                }]
            ],
            function(components, status) {
                if (status === "SUCCESS") {
                    var message = components[0];
                    var outputText = components[1];
                    // set the body of the ui:message to be the ui:outputText
                    message.set("v.body", outputText);
                    var saveconfirmation = component.find("saveconfirmation");
                    // Replace div body with the dynamic component
                    saveconfirmation.set("v.body", message);
                }
            }
        );
    }
}

No error in console. Removing ui:message also didnt worked.

Hope I am clear in explaining the issue. I looked this post also but not sure when fix will be available.

Binding to object values and arrays of objects in Lightning Locker

0

2 Answers 2

0

Something does not look right in your data binding. You declare an aura:iteration through v.milestones and you declare var="mile". But all your bindings inside the iteration are pointing to v.milestone.propertyName when they should be pointing to mile (no v. because this variable is internal to the iteration).

So first off I would try correcting that:

<aura:attribute name="milestones" type="Object[]" />
<div aura:id="saveconfirmation"></div>
<aura:iteration items="{!v.milestones}" var="mile" indexVar="index">
    <div class="slds-form-element" style="width:180px;">
        <ui:inputText label="Milestone Number" value="{!mile.name}" disabled="{!(mile.recordId != '') ? 'true' : 'false'}" class="slds-input" labelClass="slds-form-element__label" change="{!c.updateActionOnChange}"/>
    </div>
    <div class="slds-form-element" style="white-space: nowrap;width:130px">
        <ui:inputDate aura:id="dueDate" label="Due Date" value="{!mile.dueDate}" displayDatePicker="true" class="slds-input" labelClass="slds-form-element__label" blur="{!c.dateOnChange}"/>
    </div>
</aura:iteration>

<ui:button label="Save" press="{!c.saveVariationHelper}" class="slds-button slds-button--brand" aura:id="savemilestonebutton" />

Next, in your JavaScript, you declare var milestones = component.get('v.milestones'); but then you repeatedly make reference to milestoneArray which doesn't exist. I am surprised you are not seeing any console errors!

In my sandbox the fix to object binding in Lightning Locker has already rolled out so if you don't have the fix yet, you will soon. I should advise that any fields that do not already exist in the array's objects may face some binding difficulties. Sometimes I also find that if data objects were passed into the component as a parameter, Locker Service will proxy them before your code ever gets to see them and this can screw up the bindings. So in one of my components I basically had to clone the passed-in object, add necessary fields, and then assign it to a new private attribute that is only used internally.

If you made some errors in the code you posted here, please edit your original post and comment to let me know.

4
  • Is there way to check if binding fix is rolled out for my sandbox? Commented Jun 22, 2016 at 23:07
  • Someone bumped this recently, not sure who... months later, the Locker fixes have definitely been rolled out so if you're still facing this, let us know here. Also one thing I didn't catch on my first time through is, your milestones and milestone attributes should respectively be of types Map[] and Map (following the Apex convention) rather than Object (the JS convention). Doing so helps Lightning understand how to initialize the bindings correctly.
    – Charles T
    Commented Oct 20, 2016 at 5:00
  • There is nothing wrong with the attribute name the binding is fine as @Jai is using a component not directly adding elements inside the aura:iteration. Commented Sep 14, 2018 at 15:21
  • @itzmukeshy7 if you review the edit history you'll see my comment reflects the erroneous code the OP initially posted. Also this question is 2 years old and mainly about Locker Service bugs that are now fixed.
    – Charles T
    Commented Sep 14, 2018 at 16:04
0

This has been reported as a known issue. Stumbled upon it trying to solve the same problem.

https://success.salesforce.com/issues_view?id=a1p3A0000018B6LQAU

1
  • This post is over 2 years old. A lot has been changed and upgraded in that time - not the same issue.
    – Charles T
    Commented Nov 23, 2018 at 13:24

You must log in to answer this question.

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