[SalesForce] Aura Components are being aggregated on ‘items’ change of aura:iteration

I've created a simple component to show my issue:

Markup (testComp.cmp):

<aura:component description="testComp">
  <aura:attribute name="testAttr" type="List" default="[[[1, 2], [2, 3], [3, 4]]]" />
  <button onclick="{!c.doSomething}" />
  <aura:iteration items="{!v.testAttr}" var="testVal">
    <aura:iteration items="{!testVal}" var="innerVal">
      <div aura:id="innerTestDiv" />
    </aura:iteration>
  </aura:iteration>
</aura:component>

JSController (testCompController.js):

({
  doSomething : function(component) {
    var items = component.find('innerTestDiv');
    alert('Total components: ' + items.length);
    var test = component.get('v.testAttr');
    component.set('v.testAttr', test);
  }
})

Every time I click the button and set the value of "testAttr" again for the aura:iteration to iterate over it, the amount of objects returned from component.find('innerTestDiv') is increased, because the old components aren't being deleted and still seem to be on the DOM.

I've tried setting testAttr to null or an empty array before resetting it, but it doesn't help.. the moment that component.set('v.testAttr', test) is executed, the aura:iteration creates a new set of components.

Is there any way to clear the old components/elements so that only the new ones will be displayed?

I need it because I'm trying to dynamically collect the data from all the fields on the screen and currently I've done it by embedding components inside other components and just gathering information from them.. but once I encounter this issue of reiterating again, I can't rely on the created components any longer.

Best Answer

Thank you for the demo code. I have confirmed the problem and filed a bug. Here are the conditions to get the component leak:

  1. LockerService enabled
  2. Nested <aura:iteration>
  3. The inner elements of the iteration has arrays.

In other words, [[1,2,3]] is fine but [[ [ ], [ ], [ ] ]] leaks the elements in the second iterations 3 times, [[ [ ], [ ], "A"]] leaks twice etc.

The good news is that objects are not affected, [[ { }, { }, { } ]] is not leaking, so you can change the inner structure to an object.

Here is the repro code if you want to play with different scenarios:

// test.app
<aura:application>
  <aura:attribute name="mylists" type="List"/>
  <aura:attribute name="mylist" type="List"/>
  <aura:handler name="init" value="{!this}" action="{!c.init}"/>
  <button onclick="{!c.test}">Test</button>
  <aura:iteration items="{!v.mylists}" var="mylist">
    <ul aura:id="list">
    <aura:iteration items="{!mylist}" var="myitems">
        <li aura:id="item">{!myitems + ''}</li>
    </aura:iteration>
    </ul>
  </aura:iteration>
  <ul aura:id="list">
    <aura:iteration items="{!v.mylist}" var="myitems">
        <li aura:id="item">{!myitems + ''}</li>
    </aura:iteration>
  </ul>
</aura:application>

// testController.js
({
  init : function(component) {
      // component.set('v.mylists', [['A', 'B', 'C'], ['a', 'b', 'c']]); // No leak
      // component.set('v.mylists', [['A', ['Y', 'B'], 'C'], ['a', 'b', 'c']]); // Leaks 1 component
      // component.set('v.mylists', [[['X', 'A'], ['Y', 'B'], 'C'], ['a', 'b', 'c']]); // Leaks 2 component
      component.set('v.mylists', [[['X', 'A'], ['Y', 'B'], ['Z', 'C']], ['a', 'b', 'c']]); // Leaks 3 component
      // component.set('v.mylists', [[[], [], []], ['a', 'b', 'c']]); // Leaks 3 component
      // component.set('v.mylists', [[{}, {}, {}], ['a', 'b', 'c']]); // No leak
      // component.set('v.mylists', [[{length: 0}, {length: 1}, {length: 2}], ['a', 'b', 'c']]); // No leak
      component.set('v.mylist', [['X', 'A'], ['Y', 'B'], 'C']); // No leak
  },
  test : function(component) {
    var list = component.find('list');
    var items = component.find('item');
    alert('Total lists: ' + list.length + "\n" + 'Total list items: ' + items.length);
    var mylists = component.get('v.mylists');
    component.set('v.mylists', mylists);
    var mylist = component.get('v.mylist');
    component.set('v.mylist', mylist);
  }
})
Related Topic