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"
/>
Best Answer
I recommend using the Developer Console to look at the apex debug logs when encountering errors with your apex actions in lightning.
In this particular instance I believe that you are receiving an error with your query at this line of your apex controller:
The issue is with your WHERE clause. The ID should be surrounded by single quotes, so it will look like this
In addition, as mentioned in the comments of your question, you are referencing
result.error
in your error toast code without ever declaringresult
as a variable. You most likely want to callresponse.getError()
there instead, which is being passed into your callback function as a variable.