Dynamic Field Binding in Salesforce Lightning Experience
I have been playing with Lightning for some time now and recently realized that I cannot do dynamic binding in Lightning, like I used to in Visualforce.
For example, in Visualforce if I want to access a field on the object, I have two options:
- {!objectName.fieldName} – Most users familiar with Visualforce already know this.
- {!objectName[fieldName]} – This dynamically references the value without necessarily knowing the fieldname (ex. The fieldname is determined at runtime.) Read more about this here
Unfortunately, dynamic field binding is not really available in Lightning (yet),so it is not possible to build a generic component that can work on any object and any field using dynamic bindings. However, even though Lightning doesn’t support dynamic bindings yet, there are times when dynamically referencing object and field names is the most expedient development approach. I was trying to find some way to use dynamic references when an idea struck.
Below is an example of a generic detail component I built to display the fields for any object.
detail.component
[code language=”html”]
<aura:component implements="force:appHostable" controller="DetailController">
<!– attributes to populate when Component is used –>
<aura:attribute name="fieldsToShow" type="String" description="The comma separated field values"/>
<aura:attribute name="recordId" type="Id" description="The Id of the record" required="true" />
<!– Below are the attributes populated from Controller –>
<aura:attribute name="detailRecord" type="SObject" description="The detail record to display values"/>
<aura:attribute name="fieldList" type="String[]" description="the list of fields to show"/>
<!– Init –>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<h1><ui:outputText value="{!v.detailRecord.Name}"/></h1>
{!v.body}
</aura:component>
[/code]
The component itself is fairly simple, it accepts only 2 attributes:
- the recordId – the Id of the Salesforce Record and it can be any object and
- fieldsToShow – the comma separated fields you want to show
The component can be used in any app or another component like this:
[code language=”html”]
<aura:application >
<!– this is an account record –>
<c:detail recordId="001B0000007NQPs" fieldsToShow="Name,Industry,Type,Description"/>
<!– this is a contact record –>
<c:detail recordId="003B0000005YTwx" fieldsToShow="Name,Email,Phone"/>
</aura:application>
[/code]
In the original component you might have noticed I did not have any repeat tags or any code that displays the fields for an object, as I said it’s all being done in JavaScript. Below are the Controller and Helper JS files for the detail component.
Controller
[code language=”javascript”]
({
doInit : function(component, event, helper) {
helper.getRecord(component);
}
})
[/code]
The controller itself has no logic, since all it’s doing is calling the helper JS file. The helper JS file has one method, getRecord, which does the core processing.
- Reads the fieldsToShow from attribute
- Prepares the action to call the DetailController Apex class’ @AuraEnabled getRecords method.
- Calls the @AuraEnabled method and gets back the record with field values.
- If the call is successful, dynamically creates the aura:outputText component using createComponent method by passing the fieldName and the value returned for that field.
- Attaches the newly created component to the body of the detail component.
Below is the full code for the helper (I added the console statements for you to monitor the responses and flow).
Helper
[code language=”javascript”]
({
getRecord : function(component) {
var fields = component.get("v.fieldsToShow");
var recordId = component.get("v.recordId");
var action = component.get("c.getRecords");
var fieldList = fields.split(‘,’);
var fieldMap = new Object();
console.log(fieldList);
component.set("v.fieldList",fieldList);
action.setParams({
recordId:recordId,
fieldsToShow:fields
});
action.setCallback(this,function(a){
console.log(a.getReturnValue());
var sobjectrecord = a.getReturnValue();
for (var idx in fieldList) {
console.log(fieldList[idx]);
console.log(sobjectrecord[fieldList[idx]]);
$A.createComponent(
"ui:inputText",
{
"label": fieldList[idx],
"value": sobjectrecord[fieldList[idx]],
"class": "outputCls"
},
function(newCmp){
//Add the field list to the body array
if (component.isValid()) {
var body = component.get("v.body");
body.push(newCmp);
component.set("v.body", body);
}
}
);
}
component.set("v.detailRecord",a.getReturnValue());
});
$A.enqueueAction(action);
}
})
[/code]
The Apex Controller has a simple method that returns the record by performing a SOQL query.
[code language=”javascript”]
public class DetailController {
@AuraEnabled
public static Sobject getRecords(Id recordId, String fieldsToShow) {
String objectName = recordId.getSobjectType().getDescribe().getName();
String soql = ‘Select ‘ + fieldsToShow + ‘ from ‘ + objectName + ‘ where Id = :recordId’;
Sobject rec = Database.query(soql);
return rec;
}
}
[/code]
The above example does not use dynamic bindings that Visualforce does, but utilizes the way you can read the JavaScript object properties using dynamic bindings. Hopefully in near future Salesforce will figure out how to allow dynamic bindings within the component itself without using work arounds like this.
I hope you like my little trick. Please post comments or let me know if you have any work arounds that may be helpful to the Salesforce community. Happy Coding 🙂
If your team is overwhelmed or you’re looking to fast track your upcoming projects, our team of consultants is here to help. Connect with a member of our team to discuss your project, then access a deep bench of Salesforce consultants to meet your needs. Are you ready to reach for operational excellence?