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

[code]

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>

[/code]

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.

[code]

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));

}

});

}

}

})

[/code]

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:

[code]

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>

[/code]

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.

[code]

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

}

}

})

[/code]

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:

[code]

<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 */ }

})

[/code]

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.

[code]

<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>}

}

})

[/code]

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 🙂

Advertisements

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.