Display Generic sObject in Lightning

Display Generic sObject in Lightning

 

In the last post we study how we can iterate map in Lightning. There is one more thing which we face many time is dynamic field binding. Visualforce wit dynamic field binding we can dynamically create field instance but in lightning this is a major limitation.

Today we will see how we can use dynamic sObject binding in visualforce. For this we will create a sample component to display dynamic fields.

Here we will create app to get Object details. for demo I have select Account object we can select any object here. We can also select any fields here including the parent fields(cross reference field).

Now we will pass the object and field name in another component. Where we will display them on UI.

So here we are using JavaScript to to get data from the field. This is similar of what we have done in Map Iteration post.

To display on UI I am using lightning:formattedText we can also use any other component as well.

dYnamicsObject.png

You can find the complete code here as well.

DO you want to add anything let me know in comments section. Happy programming 🙂

Advertisements

Iterate Map In Lightning

Iterate Map In Lightning

Usages of Lightning is increasing day by day. We face many challenge while doing development in Lightning.  One of the challenge which I face recently is we can’t iterate map in lightning using aura:iteration.  Map is commonly used collection type which provide Key value pair to easy binding of data.

Today I will shae a code sample of how we can iterate map in Lightning easily.

First we need a Controller which will return Map to us.

 

Next we will create an APP to display It on UI.

We can easily iterate map in Javascript. So in App controller we will iterate this Map and will create a List. Now we can  easily iterate that List in the app.

This is the output which we will get.

Map Iteration

Do you like the post. Or want to say anything. Please let me know in comments section.

Deploy the Package Code using SFDX

Deploy the Package Code using SFDX

Hello Everyone, In the last post we have discussed  how we can quickly setup the SFDX. We also use the basic command to move code using SFDX, create scratch org and use them for development.

Today we will cover If we are an ISV partner so how we can best use the SFDX and how we can move our package code into scratch org to continue with development and effectively use the SFDX.

First we need to authenticate our dev hub org.  Then we will create a new project to store our project metadata.


sfdx force:project:create -n MyPkgProject

cd MyPkgProject

Now we need to authentic our package org to take a backup of code.


sfdx force:auth:web:login -a MyPkg

Now make a directory to store the metadata and fetch the data from source.


sfdx force:mdapi:retrieve -s -r ./dummy -u MyPkg -p TestData

SFDXV2 1.png

This will gives us a zip file. Which we need to unzip. Next we will convert this metadata into SFDX format.


sfdx force:mdapi:convert -r ./dummy

Next we will create a new scratch org to move our metadata there.


sfdx force:org:create -s -f config/project-scratch-def.json -a pkgscratchorg

And when our scratch org is created we are ready to push our code into new scratch org.

SFDXV2 2.png


sfdx force:source:push

And when we get the success message we can open the org and can continue with our development.


sfdx force:org:open

So within few steps we can easily move our packaging code to SFDX format and can use all the benefit of SFDX.

Did you have anything to add, Let me know in the comments section. Happy programming 🙂

 

SFDX: A New Way of Development

SFDX: A New Way of Development

In Dreamforce Salesforce announce a new development tool SFDX. It’s a new way of development where we create a temporary org for the development; In SFDX we call them Scratch org. The idea is similar with Trailhead org where we create new org for challenge and once completed we can delete them as well.

Screenshot_1.png

With recent Salesforce releases we are getting updates related to SFDX and as its GA now so I did a quick check to see how it works and how we can best use it. The main part SFDX cover is related to Source control. With SFDX and many VCS (like github) we can easily do version control. SFDX is a command line tool but we can also use Visual studio plug-in to do the development. Today we will cover how we can quickly setup the SFDX up and running.

First you need a Dev Hub org. You can signup for a trial org from here. https://developer.salesforce.com/promotions/orgs/dx-signup

macOS https://sfdc.co/sfdx_cli_osx
Windows 32-bit https://sfdc.co/sfdx_cli_win
Windows 64-bit https://sfdc.co/sfdx_cli_win64
Debian/Ubuntu 64 https://sfdc.co/sfdx_cli_linux

Download the archive from one of the URLs in the manifest, extract the archive, then run the ./install script.

Debian/Ubuntu x86 https://sfdc.co/sfdx_cli_linux_x86

Then we can download CLI from here and we need to make sure we have properly installed it.

Then first we need to do login in our devhub org and authenticate the SFDx  to use that.


sfdx force:auth:web:login -d -a DevHub

 

You can create alias for org so that it is easy to remember and can quickly type them in CLI. We can use sfdx force:org:list  to see all active org.

First you need to create a local project where you will store all your metadata.


sfdx force:project:create -n MyOrg

mage 2.png

 

Next we will create a new scratch org where we do all our development.


sfdx force:org:create -s -f config/project-scratch-def.json -a MyScratchOrg

mage 3.png

  • The -s option indicates that we want this scratch org to be the default org for this project.
  • The -f option is the path to the project scratch org configuration file.

We can also export data using SFDX to populate our new org with data.


sfdx force:data:tree:export -q "SELECT Id,Name FROM Account" -d ./data

To Import this data we can use import command.


sfdx force:data:tree:import --sobjecttreefiles data/Account.json

We can also create classes, pages, lightning component using SFDX

sfdx force:apex:class:create -n AccountController -d force-app/main/default/classes

sfdx force:lightning:component:create -n AccountLocator -d force-app/main/default/aura

To Push and Pull data from,to scratch org we use Push and Pull commands.


sfdx force:source:pull

fdx force:source:push

We can use sfdx force:org:open -u OrgAlias command to open any org in browser

To generate password for any scratch org we use the password generate command


sfdx force:user:password:generate -u OrgAlias

We can also get list of all org using list command


sfdx force:org:list

mage 4.png

To get current org details we can use org:display command.


sfdx force:org:display -u MyScratchOrg (OrgAlias)

mage 5.png

So these all are basic steps using which we can easily configure SFDX in no time. In next post we will cover How we can use Git with SFDX and How we can use Visual Studio to develop the SFDX

Did you like the post or want to add anything. Let me know in comments section. Happy Programming. 🙂

 

 

 

Lightning Data Service: Loading Data without Apex Continue

Lightning Data Service: Loading Data without Apex Continue

In the last post we study about Lightning Data service and how we can use that for Load the data and Save records.

LDS.gif

In previous post we learn about few new tags force:recordData which is base tag for LDS.  We also get saveResult in controller using that we can get State of the operation and can display different message to user based on different state. LDS also support Data caching so if user lost the connection while process data LDS save that in cache with status Draft. In this part we will continue with creating a new record using template.

Creating a Record

To create a record using Lightning Data Service,We need to declare force:recordData without assigning a recordId. Next, load a record template by calling the getNewRecord function on force:recordData. Finally, apply values to the new record, and save the record by calling the saveRecord function on force:recordData.

  1. Call getNewRecord to create an empty record from a record template. We can use this record as the backing store for a form or otherwise have its values set to data intended to be saved.
  2. Call saveRecord to commit the record. This is described in Saving a Record.

Create an Empty Record from a Record Template

To create an empty record from a record template, we don’t need to set a recordId on the force:recordData tag. Without a recordId, Lightning Data Service doesn’t load an existing record.

In our component’s init or another handler, call the getNewRecord on force:recordData.

Example: Creating a Record


ldsCreate.cmp

<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId">

<aura:attribute name="newContact" type="Object"/>

<aura:attribute name="simpleNewContact" type="Object"/>

<aura:attribute name="newContactError" type="String"/>

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

<force:recordData aura:id="contactRecordCreator"

layoutType="FULL"

targetRecord="{!v.newContact}"

targetFields="{!v.simpleNewContact}"

targetError="{!v.newContactError}" />
<div class="slds-page-header" role="banner">
<p class="slds-text-heading_label">Create Contact</p>

</div>
<!-- Display Lightning Data Service errors -->

<aura:if isTrue="{!not(empty(v.newContactError))}">
<div class="recordError">

<ui:message title="Error" severity="error" closable="true">

{!v.newContactError}

</ui:message></div>
</aura:if>

<!-- Display the new contact form -->
<div class="slds-form_stacked">

		<lightning:input aura:id="contactField" name="firstName" label="First Name"

value="{!v.simpleNewContact.FirstName}" required="true"/>

		<lightning:input aura:id="contactField" name="lastname" label="Last Name"

value="{!v.simpleNewContact.LastName}" required="true"/>

		<lightning:input aura:id="contactField" name="title" label="Title"

value="{!v.simpleNewContact.Title}" />

		<lightning:button label="Save contact" onclick="{!c.handleSaveContact}"

variant="brand" class="slds-m-top_medium"/></div>
</aura:component>

This component doesn’t set the recordId attribute of force:recordData. This tells Lightning Data Service to expect a new record. Here, that’s created in the component’s init handler.


ldsCreateController.js

({

doInit: function(component, event, helper) {

// Prepare a new record from template

component.find("contactRecordCreator").getNewRecord(

"Contact", // sObject type (objectApiName)

null, // recordTypeId

false, // skip cache?

$A.getCallback(function() {

var rec = component.get("v.newContact");

var error = component.get("v.newContactError");

if(error || (rec === null)) {

console.log("Error initializing record template: " + error);

return;

}

console.log("Record template initialized: " + rec.sobjectType);

})

);

},

handleSaveContact: function(component, event, helper) {

if(helper.validateContactForm(component)) {

component.set("v.simpleNewContact.AccountId", component.get("v.recordId"));

component.find("contactRecordCreator").saveRecord(function(saveResult) {

if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {

// record is saved successfully

var resultsToast = $A.get("e.force:showToast");

resultsToast.setParams({

"title": "Saved",

"message": "The record was saved."

});

resultsToast.fire();

} else if (saveResult.state === "INCOMPLETE") {

// handle the incomplete state

console.log("User is offline, device doesn't support drafts.");

} else if (saveResult.state === "ERROR") {

// handle the error state

console.log('Problem saving contact, error: ' +

JSON.stringify(saveResult.error));

} else {

console.log('Unknown problem, state: ' + saveResult.state + ',

error: ' + JSON.stringify(saveResult.error));

}

});

}

}

})

Deleting Record

To delete a record using Lightning Data Service, we need to call deleteRecord on the force:recordData component, and pass in a callback function to be invoked after the delete operation completes.  deleteRecord takes one argument, a callback function to be invoked when the operation completes. This callback function receives a SaveRecordResult as its only parameter. SaveRecordResult includes a state attribute that indicates success or error, and other details we can use to handle the result of the operation.

Example:


ldsDelete.cmp

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">

<aura:attribute name="recordError" type="String" access="private"/>

<force:recordData aura:id="recordHandler"

recordId="{!v.recordId}"

fields="Id"

targetError="{!v.recordError}"

recordUpdated="{!c.handleRecordUpdated}" />

<!-- Display Lightning Data Service errors, if any -->

<aura:if isTrue="{!not(empty(v.recordError))}">
<div class="recordError">

<ui:message title="Error" severity="error" closable="true">

{!v.recordError}

</ui:message></div>
</aura:if>
<div class="slds-form-element">

		<lightning:button

label="Delete Record"

onclick="{!c.handleDeleteRecord}"

variant="brand" /></div>
</aura:component>

For minimum delete process we don’t need any other fields but if we want to display data as well that we can pass them as parameter.


ldsDeleteController.js

({

handleDeleteRecord: function(component, event, helper) {

component.find("recordHandler").deleteRecord($A.getCallback(function(deleteResult) {

// NOTE: If we want a specific behavior(an action or UI behavior) when

this action is successful

// then handle that in a callback (generic logic when record is changed

should be handled in recordUpdated event handler)

if (deleteResult.state === "SUCCESS" || deleteResult.state === "DRAFT") {

// record is deleted

console.log("Record is deleted.");

} else if (deleteResult.state === "INCOMPLETE") {

console.log("User is offline, device doesn't support drafts.");

} else if (deleteResult.state === "ERROR") {

console.log('Problem deleting record, error: ' +

JSON.stringify(deleteResult.error));

} else {

console.log('Unknown problem, state: ' + deleteResult.state + ', error:

' + JSON.stringify(deleteResult.error));

}

}));

},

/**

* Control the component behavior here when record is changed (via any component)

*/

handleRecordUpdated: function(component, event, helper) {

var eventParams = event.getParams();

if(eventParams.changeType === "CHANGED") {

// record is changed

} else if(eventParams.changeType === "LOADED") {

// record is loaded in the cache

} else if(eventParams.changeType === "REMOVED") {

// record is deleted, show a toast UI message

var resultsToast = $A.get("e.force:showToast");

resultsToast.setParams({

"title": "Deleted",

"message": "The record was deleted."

});

resultsToast.fire();

} else if(eventParams.changeType === "ERROR") {

// there’s an error while loading, saving, or deleting the record

}

}

})

When the record is deleted, navigate away from the record page. Otherwise, we see a “record not found” error when the component refreshes. Here the controller uses the objectApiName property in the SaveRecordResult provided to the callback function, and navigates to the object home page.

Record Changes

If we want to perffrm any action other then rerendering we need to handle the recordUpdated event. We can handle record loaded, updated, and deleted changes, applying different actions to each change type.

For example, different actions apply to opportunities at different stages of the sales cycle.

Note: Lightning Data Service notifies listeners about data changes only if the changed fields are the same as in the listener’s fields or lawet.

Example:


<force:recordData aura:id="forceRecord"

recordId="{!v.recordId}"

lawetType="FULL"

targetRecord="{!v._record}"

targetFields="{!v.simpleRecord}"

targetError="{!v._error}"

<strong>recordUpdated="{!c.recordUpdated}" </strong>/>

Implement an action handler that handles the change.

({

recordUpdated: function(component, event, helper) {

var changeType = event.getParams().changeType;

if (changeType === "ERROR") { /* handle error; do this first! */ }

else if (changeType === "LOADED") { /* handle record load */ }

else if (changeType === "REMOVED") { /* handle record removal */ }

else if (changeType === "CHANGED") { /* handle record change */ }

})

When loading a record in edit mode, the record is not automatically updated to prevent edits currently in progress from being overwritten. To update the record, use the reloadRecord method in the action handler.


<force:recordData aura:id="forceRecord"

recordId="{!v.recordId}"

lawetType="FULL"

targetRecord="{!v._record}"

targetFields="{!v.simpleRecord}"

targetError="{!v._error}"

<strong>mode=”EDIT”</strong>

recordUpdated="{!c.recordUpdated}" />

({

recordUpdated : function(component, event, helper) {

var changeType = event.getParams().changeType;

if (changeType === "ERROR") { /* handle error; do this first! */ }

else if (changeType === "LOADED") { /* handle record load */ }

else if (changeType === "REMOVED") { /* handle record removal */ }

else if (changeType === "CHANGED") {

/* handle record change; reloadRecord will cause we to lose wer current record,

including any changes we’ve made */

<strong>component.find("forceRecord").reloadRecord();</strong>}

}

})

So we have cover all the operation which are possible using LDS. But there are still few use case where we need Apex as well. For eg:

  1. For bulk data process we need apex. So we can’t use it as StandardsetController replacement.
  2. If we need to fetch data of more than 3 level or need to display child records then we need apex for that.

Let me know what you like most about Lightning Data Service in comments. Happy programming 🙂

Lightning Data Service: Loading Data without Apex

Lightning Data Service: Loading Data without Apex

We can use Lightning Data Service to load, create, edit, or delete a record in Lightning component without using Apex code. Lightning Data Service handles Sharing rules and all other security stuff for us. As we no longer need apex for basic operations so it improve overall performance of the system.

Its a type of Standard controller for Lightning. For read only access we don’t need any code while for the Update and other DML operations JavaScript controllers is only required. It’s built on highly efficient local storage that’s shared across all components that use it. Records loaded in Lightning Data Service are cached and shared across components.

lds_architecture

Components accessing the same record see significant performance improvements, because a record is loaded only once, no matter how many components are using it. Shared records also improve user interface consistency. When one component updates a record, the other components using it are notified, and in most cases, refresh automatically.

Loading a Record

Loading a record is the simplest operation in Lightning Data Service. We can accomplish it entirely in markup. To load a record using Lightning Data Service, we need to add the force:recordData tag to our component. In the force:recordData tag, specify the ID of the record to be loaded, a list of fields, and the attribute to which to assign the loaded record. force:recordData must specify the following.

  • The ID of the record to load
  • Which component attribute to assign the loaded record
  • A list of fields to load

<aura:component

implements="flexipage:availableForRecordHome,force:lightningQuickActionWithoutHeader,

force:hasRecordId">

<aura:attribute name="record" type="Object"/>

<aura:attribute name="simpleRecord" type="Object"/>

<aura:attribute name="recordError" type="String"/>

<force:recordData aura:id="recordLoader"

recordId="{!v.recordId}"

layoutType="FULL"

targetRecord="{!v.record}"

targetFields="{!v.simpleRecord}"

targetError="{!v.recordError}"

recordUpdated="{!c.handleRecordUpdated}"

/>

<!-- Display a header with details about the record -->
<div class="slds-page-header" role="banner">
<p class="slds-text-heading_label">{!v.simpleRecord.Name}</p>

<h1 class="slds-page-header__title slds-m-right_small

slds-truncate slds-align-left">{!v.simpleRecord.BillingCity},

{!v.simpleRecord.BillingState}</h1>
</div>
<!-- Display Lightning Data Service errors, if any -->

<aura:if isTrue="{!not(empty(v.recordError))}">
<div class="recordError">

<ui:message title="Error" severity="error" closable="true">

{!v.recordError}

</ui:message></div>
</aura:if>

</aura:component>

Saving a Record

To save a record using Lightning Data Service, we need to call saveRecord on the force:recordData component, and pass in a callback function to be invoked after the save operation completes.

The Lightning Data Service save operation is used in two cases.

  • To save changes to an existing record
  • To create and save a new record

Load a Record in EDIT Mode

To load a record that might be updated, set the force:recordData tag’s mode attribute to “EDIT”. Other than explicitly setting the mode, loading a record for editing is the same as loading it for any other purpose. By default mode attribute is set to true.

Call saveRecord to Save Record Changes

To perform the save operation, call saveRecord on the force:recordData component from the appropriate controller action handler. saveRecord takes one argument—a callback function to be invoked when the operation completes. This callback function receives a SaveRecordResult as its only parameter. SaveRecordResult includes a state attribute that indicates success or error, and other details we can use to handle the result of the operation.


ldsSave.cmp

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">

<aura:attribute name="record" type="Object"/>

<aura:attribute name="simpleRecord" type="Object"/>

<aura:attribute name="recordError" type="String"/>

<force:recordData aura:id="recordHandler"

recordId="{!v.recordId}"

layoutType="FULL"

targetRecord="{!v.record}"

targetFields="{!v.simpleRecord}"

targetError="{!v.recordError}"

mode="EDIT"

recordUpdated="{!c.handleRecordUpdated}"

/>

<!-- Display a header with details about the record -->
<div class="slds-page-header" role="banner">
<p class="slds-text-heading_label">Edit Record</p>

<h1 class="slds-page-header__title slds-m-right_small

slds-truncate slds-align-left">{!v.simpleRecord.Name}</h1>
</div>
<!-- Display Lightning Data Service errors, if any -->

<aura:if isTrue="{!not(empty(v.recordError))}">
<div class="recordError">

<ui:message title="Error" severity="error" closable="true">

{!v.recordError}

</ui:message></div>
</aura:if>

<!-- Display an editing form -->

		<lightning:input aura:id="recordName" name="recordName" label="Name"

value="{!v.simpleRecord.Name}" required="true"/>

		<lightning:button label="Save Record" onclick="{!c.handleSaveRecord}"

variant="brand" class="slds-m-top_medium"/>

</aura:component>

 

As we need to save the record so for this we need to write few lines in Javascript controller


ldsSaveController.js

({

handleSaveRecord: function(component, event, helper) {

component.find("recordHandler").saveRecord($A.getCallback(function(saveResult)

{

// NOTE: If we want a specific behavior(an action or UI behavior) when

this action is successful

// then handle that in a callback (generic logic when record is changed

should be handled in recordUpdated event handler)

if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {

// handle component related logic in event handler

} else if (saveResult.state === "INCOMPLETE") {

console.log("User is offline, device doesn't support drafts.");

} else if (saveResult.state === "ERROR") {

console.log('Problem saving record, error: ' +

JSON.stringify(saveResult.error));

} else {

console.log('Unknown problem, state: ' + saveResult.state + ', error:

' + JSON.stringify(saveResult.error));

}

}));

},

/**

* Control the component behavior here when record is changed (via any component)

*/

handleRecordUpdated: function(component, event, helper) {

var eventParams = event.getParams();

if(eventParams.changeType === "CHANGED") {

// get the fields that changed for this record

var changedFields = eventParams.changedFields;

console.log('Fields that are changed: ' + JSON.stringify(changedFields));

// record is changed, so refresh the component (or other component logic)

var resultsToast = $A.get("e.force:showToast");

resultsToast.setParams({

"title": "Saved",

"message": "The record was updated."

});

resultsToast.fire();

} else if(eventParams.changeType === "LOADED") {

// record is loaded in the cache

} else if(eventParams.changeType === "REMOVED") {

// record is deleted and removed from the cache

} else if(eventParams.changeType === "ERROR") {

// there’s an error while loading, saving or deleting the record

}

}

})

In the next post we will cover the remaining point to delete the record and handle record change events. If you want to add anything let me know in comments section. Happy programming 🙂