Oh my god. It's full of code!

Lightning

Image

Salesforce Lightning DataTable Query Flattener

So I was doing some playing around with the Salesforce Lightning Datatable component and while it does make displaying query data very easy, it isn’t super robust when it comes to handling parent and child records. Just to make life easier in the future I thought it might be nice to make a function which could take a query returned by a controller and ‘flatten’ it so that all the data was available to the data table since it cannot access nested arrays or objects. Of course the table itself doesn’t have a way to iterate over nested rows so the child array flatted function is not quite as useful (unless say you wanted to show a contacts most recent case or something). Anyway, hopefully this will save you some time from having to write wrapper classes or having to skip using the data table if you have parent or child nested data.

Apex Controller

public with sharing class ManageContactsController {

    @AuraEnabled
    public static list<Contact> getContacts()
    {
        return [select firstname, name, lastname, email, phone, Owner.name, Owner.Profile.Name, (select id, subject from cases limit 1 order by createdDate desc ) from contact];
    }
}

Lightning Controller

({
   init: function (component, event, helper) {
        component.set('v.mycolumns', [
                {label: 'Contact Name', fieldName: 'Name', type: 'text'},
                {label: 'Phone', fieldName: 'Phone', type: 'phone'},
                {label: 'Email', fieldName: 'Email', type: 'email'},
            	{label: 'Owner', fieldName: 'Owner_Name', type: 'text'},
            	{label: 'Most Recent Case', fieldName: 'Cases_0_Subject', type: 'text'}
            ]);
        helper.getData(component, event, 'getContacts', 'mydata');
    }
})

Helper

({
    flattenObject : function(propName, obj)
    {
        var flatObject = [];
        
        for(var prop in obj)
        {
            //if this property is an object, we need to flatten again
            var propIsNumber = isNaN(propName);
            var preAppend = propIsNumber ? propName+'_' : '';
            if(typeof obj[prop] == 'object')
            {
                flatObject[preAppend+prop] = Object.assign(flatObject, this.flattenObject(preAppend+prop,obj[prop]) );

            }    
            else
            {
                flatObject[preAppend+prop] = obj[prop];
            }
        }
        return flatObject;
    },
    
	flattenQueryResult : function(listOfObjects) {
        if(typeof listOfObjects != 'Array') 
        {
        	var listOfObjects = [listOfObjects];
        }
        
        console.log('List of Objects is now....');
        console.log(listOfObjects);
        for(var i = 0; i < listOfObjects.length; i++)
        {
            var obj = listOfObjects[i];
            for(var prop in obj)
            {      
                if(!obj.hasOwnProperty(prop)) continue;
                if(typeof obj[prop] == 'object' && typeof obj[prop] != 'Array')
                {
					obj = Object.assign(obj, this.flattenObject(prop,obj[prop]));
                }
                else if(typeof obj[prop] == 'Array')
                {
                    for(var j = 0; j < obj[prop].length; j++)
                    {
                        obj[prop+'_'+j] = Object.assign(obj,this.flattenObject(prop,obj[prop]));
                    }
                }
        	}
        }
        return listOfObjects;
    },
    getInfo : function(component, event, methodName, targetAttribute) {
        var action = component.get('c.'+methodName);
        action.setCallback(this, $A.getCallback(function (response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                console.log('Got Raw Response for ' + methodName + ' ' + targetAttribute);
                console.log(response.getReturnValue());
                
                var flattenedObject = this.flattenQueryResult(response.getReturnValue());
                
                component.set('v.'+targetAttribute, flattenedObject);
                
                console.log(flattenedObject);
            } else if (state === "ERROR") {
                var errors = response.getError();
                console.error(errors);
            }
        }));
        $A.enqueueAction(action);
    }
})

Component (Sorry my code highlighter didn’t like trying to parse this)

<aura:component controller=”ManageContactsController” implements=”forceCommunity:availableForAllPageTypes” access=”global”>
<aura:attribute name=”mydata” type=”Object”/>
<aura:attribute name=”mycolumns” type=”List”/>
<aura:handler name=”init” value=”{! this }” action=”{! c.init }”/>
<h3>Contacts (With Sharing Applied)</h3>
<lightning:datatable data=”{! v.mydata }”
columns=”{! v.mycolumns }”
keyField=”Id”
hideCheckboxColumn=”true”/>
</aura:component>

Result

Hope this helps!


Lightning Update List of Records Workaround (Quick Fix)

I’ve been doing some work with Salesforce Lightning, and so far it is certainly proving… challenging. I ran into an issue the other day to which I could find no obvious solution. I was attempting to pass a set of records from my javascript controller to the Apex controller for upsert. However it was throwing an error about ‘upsert not allowed on generic sObject list’ or something of that nature when the list of sObjects was in-fact defined as a specific type. After messing around with some various attempts at casting the list and modifying objects in the javascript controller before passing to the apex to have types I couldn’t find an elegant solution. Instead I found a workaround of simply creating a new list of the proper object type and adding the passed in records to it. I feel like there is probably a ‘proper’ way to make this work, but it works for me, so I figured I’d share.

//***************** Helper *************************//
	saveMappingFields : function(component,fieldObjects,callback)
	{

        var action = component.get("c.saveMappingFields");
        action.setParams({
            fieldObjects: fieldObjects
        });        
        action.setCallback(this, function(actionResult){
         
            if (typeof callback === "function") {
            	callback(actionResult);
            }
        });  
        
        $A.enqueueAction(action);             
	}
	
//**************** Apex Controller **********************//
//FAILS
@AuraEnabled
global static string saveMappingFields(list<Mapping_Field__c> fieldObjects)
{
	list<database.upsertResult> saveFieldResults = database.upsert(fieldObjects,false);	
}

//WORKS
@AuraEnabled
global static string saveMappingFields(list<Mapping_Field__c> fieldObjects)
{
	list<Mapping_Field__c> fixedMappingFields = new list<Mapping_Field__c>(fieldObjects);
	
	list<database.upsertResult> saveFieldResults = database.upsert(fixedMappingFields,false);	
}