I am trying to create a simple, reusable component that allows me to list related records in an easy to read way. I want to be able to use this component a few times on a single record, and I would like it to be able to be used for different target objects.
My basic thought is to pull the current record, define target parameters, do a server query, and then return the results through aura:iteration. I need to be able to create the server side controller in such a way that it can query for any object and return them the same way.
I have the code built, but right now I am getting a really generic Uncaught Error in $A.getCallback() [result is not defined] message. I've been googling what that could mean in my case and it seems to always be a typo or inconsistent variable names. If that is my problem, I am unable to find the issue.
I do wonder if the problem is the way I return the information from the server. Could the problem be the wrapper class being put into a list? That doesn't seem right....
Also, somewhat related. As you can tell from the name of the component, I had planned on iterating over lightning:tile because I thought that would look nice. But I can't seem to find an example of how that would be accomplished. Does anyone have any examples of iterating over lightning:tile?
Here is the code I have:
relRecordTiles.cmp
<aura:component controller="relRecordTilesController" access="global">
<!--<aura:attribute name="resultItems" type="Object" access="public"
description="This attribute can be used by parent component to read selected records"/> -->
<aura:attribute name="currentID" type="String" access="public"
description="ID of current record (which is the parent of the return values)"/>
<aura:attribute name="objectName" type="String" access="public"
description="Name of Object to be searched"/>
<aura:attribute name="lookupField" type="String" access="public"
description="Name of field that connects the child object to the parent (current record)"/>
<aura:attribute name="field1" type="String" access="public"
description="API Name of the first field, to be used to show text"/>
<aura:attribute name="field1Label" type="String" access="public"
description="Label for the value returned"/>
<aura:attribute name="field2" type="String" access="public"
description="API Name of the second field, to be used to show text"/>
<aura:attribute name="field2Label" type="String" access="public"
description="Label for the value returned"/>
<aura:attribute name="field3" type="String" access="public"
description="API Name of the third field, to be used to show text"/>
<aura:attribute name="field3Label" type="String" access="public"
description="Label for the value returned"/>
<aura:attribute Name="serverResult" type="list" />
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<br/>
relRecordTiles should go here:
<div class="salesforce slds">
<ul class="slds-has-dividers--around-space">
<aura:iteration items="{!v.serverResult}" var="item">
<li class="slds-item">
<div class="slds-tile slds-tile--board">
<h3 class="slds-truncate"><a href="{! '# ' + objectName + '/' + item.Id}"> {!objectName}: {!item.Name}</a> </h3>
<div class="slds-tile__detail slds-text-body--small">
<aura:if isTrue="{!not(empty(v.field1))}">
<p>{!field1Label}: {!item.field1}</p> <br/>
</aura:if>
<aura:if isTrue="{!not(empty(v.field2))}">
<p>{!field2Label}: {!item.field2}</p> <br/>
</aura:if>
<aura:if isTrue="{!not(empty(v.field3))}">
<p>{!field3Label}: {!item.field3}</p> <br/>
</aura:if>
</div>
</div>
</li>
</aura:iteration>
</ul>
</div>
relRecordTiles.js
({
doInit : function(component, event, helper) {
console.log("relRecordTiles is starting up...");
//Pull values from component
//var currentID = component.get("v.currentID");
//var objectName = component.get("v.objectName");
//var lookupField = component.get("v.lookupField");
//var field1 = component.get("v.field1");
//var field2 = component.get("v.field2");
//var field3 = component.get("v.field3");
//call relRecordTilesController.getResults server side controller
var action = component.get('c.getResults');
//Set the variables for the server side controller
action.setParams({
currentID : component.get("v.currentID"),
objectName : component.get("v.objectName"),
lookupField : component.get("v.lookupField"),
field1 : component.get("v.field1"),
field2 : component.get("v.field2"),
field3 : component.get("v.field3")
});
// Set up the callback
action.setCallback(this, $A.getCallback(function (response) {
var state = response.getState();
var resultsToast = $A.get("e.force:showToast");
if(state === "SUCCESS"){
//if successful stores query results in serverResult
component.set('v.serverResult', response.getReturnValue());
} else if (state === "ERROR") {
//otherwise write errors to console for debugging
alert('Problem with connection. Please try again. Error Code: relRecordTiles.doInit.action.setCallback');
resultsToast.setParams({
"title": "Error",
"message": "relRecordTiles failed to load due to: " + JSON.stringify(result.error)
});
resultsToast.fire();
var errors = response.getError();
console.error(errors);
}
}));
$A.enqueueAction(action);
}
})
relRecordTilesController.apxc
public class relRecordTilesController {
@AuraEnabled
public static List<tileWrapper> getResults(id currentID, string objectName, string lookupField, string field1, string field2, string field3) {
System.debug('getResults starting...');
//add in query fields if they are not null
string queryfields;
if(field1 != null){queryfields = ', ' +field1;}
if(field2 != null){queryfields = queryfields+ ', ' +field2;}
if(field3 != null){queryfields = queryfields+ ', ' +field3;}
//build the string we will query
string queryString = 'SELECT id, name' +queryfields+ ' FROM ' +objectName+ ' WHERE ' +lookupField+ '=' +currentID;
//get the data into a list
List<sObject> queryOutput = new List<sObject>();
queryOutput = database.query(queryString);
system.debug('queryOutput = ' + queryOutput);
//Iterate over the list and put the values into a wrapper class defined below
List<tileWrapper> returnList = new List<tileWrapper>();
for(sObject s: queryOutput){
tileWrapper tile = new tileWrapper();
tile.recID = String.valueOf(s.id);
tile.recName = String.valueOf(s.get('name'));
if(String.valueOf(s.get(field1)) != null ){tile.field1 = String.valueOf(s.get(field1));}
if(String.valueOf(s.get(field2)) != null ){tile.field2 = String.valueOf(s.get(field2));}
if(String.valueOf(s.get(field3)) != null ){tile.field3 = String.valueOf(s.get(field3));}
returnList.add(tile);
}
system.debug('returnList = ' + returnList);
return returnList;
}
public class tileWrapper{
public id recID{get;set;}
public string recName{get;set;}
public string field1{get;set;}
public string field2{get;set;}
public string field3{get;set;}
}
}
Calling component from parent Component
<lightning:formattedText value="Related Divisions" />
<c:relRecordTiles currentID="{!v.selItem}"
objectName="Division2Contact__c"
lookupField="Contact__c"
field1="Full_Division_Name_Backward__c"
field1Label="Division"
field2="Division_Campus__c"
field2Label="Campus"
field3="Status__c"
field3Label="Status"
/>
var resultin yourrelRecordTiles.js, but in your error handling, you try to callJSON.stringify(result.error), without a variable for result. Did you meanresponse.error?relRecordTiles failed to load due to: undefinedwhich is better, but still not functioning.state == 'ERROR', then its not null, and clearly failed somewhere. Im not really versed in lightning otherwise I'd have posted an answer by now, hopefully at least this helps.