[SalesForce] Nested aura:iteration to dynamically get fields

I am trying to dynamically create a data table with two arrays (SObject array and a String array which has field API name).

The two arrays are Contact[] and String[], where String[] consists of ["Id", "Name"].

What I've tried dot notation and square bracket notation but neither worked.

<!-- row -->
<aura:iteration item="{!v.contacts} var="contact">
    <!-- column -->
    <aura:iteration item="{!v.fieldNames} var="fieldName">
        {!contact.fieldName} 
        {!contact[fieldName]}
    </aura:iteration>
</aura:iteration>

Not quite sure what is the correct approach to do this.

Best Answer

As far as I can tell, Lightning components don't support that dynamic reference syntax.


You could consider dynamically generating the content of this component with the component controller:

Component

<aura:component controller="GetAFewContacts">
    <aura:attribute name="contacts" type="Contact[]" />
    <aura:attribute name="fieldNames" type="String[]" default='["Id","Name"]'/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    {!v.body}
</aura:component>

Client-side Controller

({
    doInit : function(component, event, helper) {
        var action = component.get("c.getContacts");
        action.setCallback(this,function(response){
                if (response.getState() === "SUCCESS"){
                    console.log(response.getReturnValue());
                    component.set("{!v.contacts}",response.getReturnValue());

                    var contacts = component.get("{!v.contacts}");
                    var fieldNames = component.get("{!v.fieldNames}");
                    var NewBody = "";
                    for (var i=0; i<contacts.length; i++){
                        for (var ii=0; ii<fieldNames.length; ii++){
                            NewBody += contacts[i][fieldNames[ii]];
                        }
                        NewBody += "\n";
                    }
                    console.log(NewBody);
                    $A.createComponent(
                        "ui:outputText",
                        {
                            "value" : NewBody
                        },
                        function(newComponent){
                            component.set("v.body",newComponent);
                        }
                    )
                }
            }
        );
        $A.enqueueAction(action);
    }
})

Server-side Controller

public class GetAFewContacts {

    @AuraEnabled
    public static list<Contact> getContacts(){
        return [SELECT Id, Name FROM Contact LIMIT 10];
    }

}

Or you could use a second component that sets the value through the component's renderer:

Component

<aura:component controller="GetAFewContacts">
    <aura:attribute name="contacts" type="Contact[]" />
    <aura:attribute name="fieldNames" type="String[]" default='["Id","Name"]'/>

    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <aura:iteration items="{!v.contacts}" var="contact" >
        <aura:iteration items="{!v.fieldNames}" var="fieldName">

            <analysis:ContactAndFieldName contact="{!contact}" fieldName="{!fieldName}" />

        </aura:iteration>
    </aura:iteration>

</aura:component>

Client-side Controller

({
    doInit : function(component, event, helper) {
        var action = component.get("c.getContacts");
        action.setCallback(this,function(response){
                if (response.getState() === "SUCCESS"){
                    component.set("{!v.contacts}",response.getReturnValue());
                }
            }
        );
        $A.enqueueAction(action);
    }
})

Server-side Controller

public class GetAFewContacts {

    @AuraEnabled
    public static list<Contact> getContacts(){
        return [SELECT Id, Name FROM Contact LIMIT 10];
    }

}

Component -- ContactAndFieldName

<aura:component >
    <aura:attribute name="contact" type="Contact" />
    <aura:attribute name="fieldName" type="String" />

    <ui:outputText aura:Id="outputTextId" />

</aura:component>

ContactAndFieldNameRenderer

({

    render : function(component, helper) {
        var ret = this.superRender();

        var Contact = component.get('v.contact');
        var FieldName = component.get('v.fieldName');
        var outputText = component.find("outputTextId");
        outputText.set("v.value",Contact[FieldName]);

        return ret;
    },

})

If an init event is used instead of putting the javascript in the renderer:

Component -- ContactAndFieldName

<aura:component >
    <aura:attribute name="contact" type="Contact" />
    <aura:attribute name="fieldName" type="String" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <ui:outputText aura:Id="outputTextId" />

</aura:component>

ContactAndFieldNameController

({
    doInit : function(component, helper) {
        var Contact = component.get('v.contact');
        var FieldName = component.get('v.fieldName');
        var outputText = component.find("outputTextId");
        outputText.set("v.value",Contact[FieldName]);
    }
})

(no custom renderer, and everything else is the same)

Related Topic