3

Another issue that I found with locker service, I presume this is some kind of framework bug related to locker service (if someone can tell, I'd appreciate).

I have a simple component which has one attribute - item, which has one property errors that is array of objects. In the component I display errors using aura:iteration.

The problem happens when I will modify item.errors (in this example after calling rerender controller function.

In the console.log I can see the array has 3 elements (which is correct): Console.log result

But on the screen I can see only one element being rendered: Screen result

Component code:

<aura:component >

    <aura:attribute name="item" type="Object" access="global"/>
    <aura:handler name="init" value="{!this}" action="{!c.initialize}"/>

    <h1>This is sample component to show problem of aura:iteration and binding of attributes</h1>
    <a onclick="{!c.rerender}">RERENDER</a>

    <aura:if isTrue="{!v.item != null}">
        <span>
        <aura:iteration items="{!v.item.errors}" var="err">
            <span>
                <aura:if isTrue="{!err.display}">
                    <div>{!err.message}</div>
                </aura:if>
            </span>
        </aura:iteration>
        </span>
    </aura:if>

</aura:component>

Controller Code:

({
    initialize : function(component, event, helper) {
        var item = {};

        var errs = [];
        var err1 = {};
        err1.display = true;
        err1.message = 'test error';

        errs.push(err1);
        item.errors = errs;
        component.set("v.item", item);
    },
    rerender : function(component, event, helper) {
        var item = component.get("v.item");

        item.errors = [];
        var err1 = {};
        err1.display = true;
        err1.message = 'test error';
        item.errors.push(err1);

        var err2 = {};
        err2.display = true;
        err2.message = 'test error 2';
        item.errors.push(err2);

        var err3 = {};
        err3.display = true;
        err3.message = 'test error 3';
        item.errors.push(err3);

        component.set("v.item", item);

        console.log(component.get("v.item"));
    }
})

EDIT: I suspect that it may come from the fact that newly added array elements were not wrapped into secure object (only 1st item was wrapped, 2nd and 3rd were not)?

Array Elements

EDIT 2: This doesn't happen when you iterate over an object[] attribute directly. In that case everything works as expected. The problem only happens when you iterate over a collection that is a property of some object:

This would work fine:

<aura:attribute name="errors" type="Object[]" access="global"/>
<aura:iteration items="{!v.errors}" var="err" indexVar="i">
    <span>
        <aura:if isTrue="{!err.display}">
            <div>{!err.message}</div>
        </aura:if>
    </span>
</aura:iteration>
  • It's not resolved for me. I'm seeing this issue in a production org. I have an iteration over a menuItem list, then look up some user information to determine whether to show or hide menu items, but the iteration never rerenders after the first time. I'm wondering if it's related to W-3458193 mentioned here: salesforce.stackexchange.com/questions/147740/… - the comment: > I believe the issue here is at the intersection of aura:iteration and Locker resulting in a sort of custody battle over ownership of the resulting DOM. Opened bug – liquidleaf Dec 15 '16 at 18:23
2

If you replace the first line of your rerender function with:

var item = {};

It successfully pushes all 3 errors to the view. So there are some problems with adding new members to the array inside the SecureObject, but if you push in a brand new object it's happy.

Note also that if you're sneaky enough to do:

component.set('v.item.errors', item.errors);

Then it appears to refresh the bindings to the array objects.

  • But wouldn't then item be a different instance of the object? That's not necessarily what I want to do. I'll try the second sneaky solution tomorrow. It would be also good to know if this is going to be fixed some day. – Maciek Simm Jul 7 '16 at 20:25
  • It would appear that the second sneaky solution does connect the updated data to the enclosing object. And yes the first line is not so much a solution as proof that the trouble lies in getting an object back out of the view and reusing it. – Charles T Jul 7 '16 at 20:48
  • I've tried that and it seems to work. Do you have any workaround for situations where you have an object with array of objects, where one property is also an array? Naively I tried: component.set("v.item.components[0].componentDefinition", item.components[0].componentDefinition); but it didn't worked – Maciek Simm Jul 8 '16 at 14:12
  • No idea... the best I can come up with is to try having a private aura:attribute, let's say of type Map, and that it's called fixer... then try component.set("v.fixer", item.components[0]); component.set("v.fixer.componentDefinition", item.components[0].componentDefinition); ... and if necessary, then component.set("v.item.components", item.components); ... and hopefully it creates the needed bindings? – Charles T Jul 8 '16 at 15:43
  • That sounds like an overkill, but thanks for suggestion. I spoke today with a sucess manager frm SFDC, he promised to get hold of some technical contact next week. I'll try to underline these two issue with bindings and uninitialized attributes, because current workarounds are a bit painful. – Maciek Simm Jul 8 '16 at 21:33
1

I am facing a similar issue when I update an Array Attribute which is being rendered inside of aura:iteration. When I add a property to the array's item value and set the attribute

"component.set('v.arrAttr', arrAttr)"

the arrAttr doesnt get updated and isnt rerendered. my work around is that I set arrAttr to null and then set to actual value in next line.

Work Around

"component.set('v.arrAttr', null)" "component.set('v.arrAttr', arrAttr)"

  • Also, if setting your component.set('v.arrAttr', null) triggers a re-render and your component throws an error because arrAttr is null. You could resolve that by adding it inside a try-catch-finally block to make sure arrAttr is set to correct value. try { component.set('v.arrAttr', null); } catch(e) { // whatever } finally { component.set('v.arrAttr', arrAttr); } – Zia Minhas Jan 30 at 20:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.