2

Scenario

In Visualforce we had strong patterns for implementing the rendering of hierarchical structures. As a simplified example (standing for many other use cases) let's say we want to show all Account with their Contacts as a tree-like output.

In Visualforce it is so super easy:

public class elfAccountContacts {
    public Account[] accounts { get; set; }
    public PageReference init() {
        this.accounts = [select Id, Name, (select Id, LastName from Contacts) from Account];
        return null;
    }
}

<apex:page controller="elfAccountContacts" action="{!init}">
    <apex:repeat var="account" value="{!accounts}">
        <div>{!account.Name}<ul>
            <apex:repeat var="contact" value="{!account.contacts}">
                <li>{!contact.LastName}</li>
            </apex:repeat>
        </ul></div>
    </apex:repeat>
</apex:page>

Bam! It looks like this

enter image description here

Now I thought: what the heck, in Lightning it should be easy, too. So I did this:

public class elfAccountContacts {
    @AuraEnabled public static Account[] loadAccounts() {
        return [ select Id, Name, (select Id, LastName from Contacts) from Account];
    }
}

<aura:component controller="elfAccountContacts" implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global" >
    <aura:attribute name="accounts" type="Account[]" default="" />
    <ltng:require afterScriptsLoaded="{!c.afterScriptsLoaded}" scripts=""/>
    <aura:iteration items="{! v.accounts }" var="account">
        <div>{!account.Name}<ul>
            <aura:iteration items="{! v.account.Contacts }" var="contact">
                <li>{!contact.Name}</li>
            </aura:iteration>
        </ul></div>
    </aura:iteration>
</aura:component>

afterScriptsLoaded : function(cmp, evt, hlp) {
    action = cmp.get("c.loadAccounts");
    action.setCallback( null, function(callbackResult) {
        if(callbackResult.getState()=='SUCCESS') {
            console.log('vanilla', callbackResult.getReturnValue()  );
            cmp.set("v.accounts",callbackResult.getReturnValue() );
        }
    });
    $A.enqueueAction( action );
},

As an unfortunate, using much more code brings only less results. I get the top-level accounts, I get the contacts console logged (so I'm 100% sure that they are there) but I get rendered only the accounts:

enter image description here

Why the contacts are not rendered? What am I doing so wrong here?

  • You are querying LastNamebut presenting Name. But Jason's answer looks like the fundamental cause. Personally hate this v. thing. – Keith C Sep 1 '17 at 18:30
  • @KeithC LastName for Contact and Name for Account – Uwe Heim Sep 1 '17 at 18:33
  • {!contact.Name} in the component markup though. – Keith C Sep 1 '17 at 18:34
  • @KeithC you and Jason where right! Now it works. +1 for HATE on that "v."-thing. This was the most error-prone idea someone came up with... – Uwe Heim Sep 1 '17 at 18:36
  • You have more lightning mistakes in here, by the way :p – sfdcfox Sep 1 '17 at 18:38
6

in your second iteration, use {!account.Contacts} instead of {!v.account.Contacts}

<aura:iteration items="{!account.Contacts}" var="contact">
  • Great! Thanks, it works now! But it's so painful writing code like this... – Uwe Heim Sep 1 '17 at 18:37

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.