[SalesForce] Lightning: Bug in Lightning framework when using aura:renderIf

I have a Nav component which contains the following:

<aura:renderIf isTrue="{!v.showAllProducts}">
    <c:showAllProductsCmp/>

    <aura:set attribute="else">
        <c:showSelectedProductsCmp/>
    <aura:set>
<aura:renderIf>

On a button press, showAllProducts changes from false -> true, and true -> false.

What this does is allow a user to see all products and select them (this is done in the showAllProductsCmp component. The component simply uses <aura:iteration> to render all product objects.)

and then switch to see only the selected products with the option to unselect them (in the showSelectedProductsCmp component).

I can add and remove any/all products, and my code works as intended — as long as there is at least one product remaining. For example, I can add 4 products, remove 3, and it works perfectly.

As soon as I remove the last product in the showSelectedProductsCmp and then try to switch back over to the showAllProductsCmp, the app crashes and I get a javascript error:

Uncaught TypeError: Cannot read property 'childNodes' of null
aura_proddebug.js:8364

This line is part of the Lightning framework. Specifically it is:

nextSibling = target.childNodes[calculatedPosition];

So the target variable is null and it crashes.

If I remove renderIf and set attribute="else" components and ALWAYS render my two custom components, everything works as it should and there are no errors — even if I remove all products.

So I don't believe that there is anything wrong with my code, though I would be happy to be corrected 🙂

PS: This doesn't happen on all renderIf statements: I have quite a few of them in my code and all of them work except for this one. Which means that this is hard to reproduce, sadly.

[Edit] I guess I should note that this nav component comes after quite a few other renderIf components, so maybe Lightning struggles with more than a couple renderIf components. Another person has a similar problem to mine, and s/he also has multiple renderIf statements before the one that finally breaks. developer.salesforce.com

[Edit2] Here is how you can reproduce the error (I'm pretty sure):
Create a new Lightning application and copy the following code (which contains a lot of renderIfs)

[Edit3] The previous example had an error that was my fault. I've made a new example that hopefully doesn't have an error that is my fault. 🙂

<aura:application >
<aura:attribute name="isEmptyArray" type="Boolean" default="false"/>
<aura:attribute name="testingArray" type="Opportunity[]"/>

<button onclick="{!c.fillArray}">Fill array</button>
<button onclick="{!c.emptyArray}">Empty Array</button>

<div>
    <aura:renderIf isTrue="{!v.testingArray.length > 0}">
        <aura:iteration items="{!v.testingArray}" var="opp">
            {!opp.name} <br/>
        </aura:iteration>
        <aura:set attribute="else">
            Array empty
        </aura:set>
    </aura:renderIf>
</div>
</aura:application>

And then copy the following code into the application controller:

({
fillArray:function(component,event,helper) {
    var tempArray = [{'sobjectType': 'Opportunity', 'name':'One'},{'sobjectType': 'Opportunity', 'name':'Two'}];
    component.set("v.testingArray", tempArray);
},
emptyArray:function(component,event,helper) {
    var emptyArray = [];
    component.set("v.testingArray", emptyArray);
}
})

Click the "fill array" button, then the "empty array" button, then the "fill array" button again. Receive the error.

Best Answer

This is a known issue in the Framework.

We do plan to fix it, but there is a workaround and so it's not #1 on our plate over things like Performance.

Short Story

<aura:renderIf isTrue="{!v.testingArray.length > 0}">
        <SPAN>
        <aura:iteration items="{!v.testingArray}" var="opp">
            {!opp.name} <br/>
        </aura:iteration>
        </SPAN>
        <aura:set attribute="else">
            Array empty
        </aura:set>
    </aura:renderIf>

Long Story

The issue here is that some components like aura:renderIf and aura:iteration may not actually render anything. But you can come along later, change a condition and then they render.

To know where to render when it comes time to do so, we add a comment into the page. Then when we have some output, we replace the comment with the output.

But when you get a component like iteration which uses a comment, inside another component that uses a comment the framework gets confused. We replace the comment with the content of the iteration, but then renderIf loses its marker comment and you've hit the error.

Wrapping the iteration in a span causes iteration and renderIf not to share markers and things start working again.

Something we plan to fix, but the marker system is so complex we have to be careful not to break other things.