function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Afzaal HassanAfzaal Hassan 

Lightning component not iterating list

Hello
I wrote a lightning/aura component. This component lists all the orders associated with a case. When I running a SOQL query, I am getting 5 unique orders for a particular case, for example. But when I am looking at the lightning component, its displaying the same order 5 times. So I am thinking that the aura component is not iterating the apex list properly. I have the code below. Can anyone pinpoint, where I might have gone wrong?

Apex controller:
public class CC_OrderListController {
    
    @AuraEnabled
    public static List <Order> getOrders(Id caseId) {
        Case c = [select ContactId from Case where Id = :caseId LIMIT 1];
        Id con = c.ContactId;
        system.debug(con);
        
        List<Order> result = [SELECT Id, BillToContact.Email, EffectiveDate, Sub_Total__c, External_Order_URL__c FROM Order where BillToContactId = :con ORDER BY createdDate DESC LIMIT 20];
        system.debug(result);
        return (result == null ? new List<Order>() : result);
    }
    
}

Aura component:
<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" controller="CC_OrderListController" access="global">
    
    <aura:attribute name="data" type="Object"/>
    <aura:attribute name="columns" type="List"/>

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


    <div style="min-height: 200px;">
        <lightning:datatable
                keyField="id"
                data="{! v.data }"
                columns="{! v.columns }"
                hideCheckboxColumn="true"/>
    </div>
    
</aura:component>

Aura JS controller:
({
    init : function(component, event, helper) {
        
        component.set('v.columns', [
            {label: 'Email', fieldName: 'Email', type: 'text'},
            {label: 'Order Date', fieldName: 'EffectiveDate', type: 'Date'},
            {label: 'Sub Total', fieldName: 'Sub_Total__c', type: 'currency'},
            {label: 'Order Link', fieldName: 'orderLink', type: 'url'}
        ]);

        var fetchData = {
            Email: "",
            EffectiveDate : "",
            Sub_Total__c : "",
            orderLink: ""
        };


        helper.fetchData(component,fetchData);
        
    }
})

JS Helper method:
({
    fetchData : function(component, fetchData) {
        var action = component.get('c.getOrders');
        action.setParams({ "caseId" : component.get('v.recordId') });
        
        var self = this;
        var results = [];
        action.setCallback(this, function(actionResult) {
            var res = actionResult.getReturnValue();
            for(var i = 0; i < res.length;i++){
                fetchData.Email = res[i].BillToContact.Email;
                fetchData.EffectiveDate = res[i].EffectiveDate;
                fetchData.Sub_Total__c = res[i].Sub_Total__c;
                fetchData.orderLink = res[i].External_Order_URL__c;
                
                results.push(fetchData);
            }
            
            component.set('v.data', results)
        });
        $A.enqueueAction(action);
    }
})

Should I use aura iteration tag ? Any help would be greatly appreciated. Thank you
Best Answer chosen by Afzaal Hassan
Alain CabonAlain Cabon
@Afzaal Hassan

It is strange. 
What do you see in the console log?

action.setCallback(this, function(actionResult) {
            let res = actionResult.getReturnValue();
            for(let i = 0; i < res.length;i++){
                   let fetchData = {};
                   fetchData.Email = res[i].BillToContact.Email;
                   fetchData.EffectiveDate = res[i].EffectiveDate;
                   fetchData.Sub_Total__c = res[i].Sub_Total__c;
                   fetchData.orderLink = res[i].External_Order_URL__c;
                   console.log('fetchData: ' + i + ')' + JSON.stringify( fetchData )) ;
                   results.push(fetchData);
               }
 

All Answers

Alain CabonAlain Cabon
@Afzaal Hassan

It is strange. 
What do you see in the console log?

action.setCallback(this, function(actionResult) {
            let res = actionResult.getReturnValue();
            for(let i = 0; i < res.length;i++){
                   let fetchData = {};
                   fetchData.Email = res[i].BillToContact.Email;
                   fetchData.EffectiveDate = res[i].EffectiveDate;
                   fetchData.Sub_Total__c = res[i].Sub_Total__c;
                   fetchData.orderLink = res[i].External_Order_URL__c;
                   console.log('fetchData: ' + i + ')' + JSON.stringify( fetchData )) ;
                   results.push(fetchData);
               }
 
This was selected as the best answer
Afzaal HassanAfzaal Hassan
@Alain Cabon Thank you for your advice. The iteration seems to be working now. But can you please explain how these minor changes was able t fix the problem?
Alain CabonAlain Cabon
@Afzaal Hassan

Javascript is filled with some traps above all with var and the closures (not intuitive for the used variables inside the function and a complicated underlying mechanism).

1) Callback Functions Are Closures  When we pass a callback function as an argument to another function, the callback is executed at some point inside the containing function’s body just as if the callback were defined in the containing function.
https://javascriptissexy.com/understand-javascript-callback-functions-and-use-them/
 
action.setCallback(this, function(actionResult) { ... } );

2) Scoping & Hoisting in JavaScript
  • All variables and constants that need to be visible within the scope of a particular function should be declared using ‘var’ and ‘const’ keywords respectively at the top of that function.
  • Inside blocks (conditional statements or loops) variables and constants should be declared on the top of the block using ‘let’ and ‘const’ respectively.
https://www.geeksforgeeks.org/scoping-hoisting-javascript/

When you combine "hoisting" and "closures", the code could failed in a not intuitive way (everything appears to be correct) and the simple test to do is to replace var with let (that is often sufficient) to be sure of the scope of the variables.
 
Alain CabonAlain Cabon
Hoisting:
var x = 10;
  
function test()
{
    if (x > 20) {
        var x = 50;
    }  
    console.log(x);  
}  
test();  // 10 or 'undefined' on the console?

If you have guessed 10 following the scoping logic discussed in the previous examples, then you are unfortunate as 10 is not the correct answer. This time it will print ‘undefined’ on the console. This is because of the combined effect of variable scoping and variable hoisting.

Not intuitive for just hoisting but if you add a closure moreover that becomes evil.
 
Alain CabonAlain Cabon
Closure Scope - ChainSection

For every closure we have three scopes:-
  • Local Scope (Own scope)
  • Outer Functions Scope
  • Global Scope

So, we have access to all three scopes for a closure but often make a common mistake when we have nested inner functions.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures