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 🙂

Advertisements

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 🙂

Lightning Events: Detail Overview Part 2

Lightning Events: Detail Overview Part 2

In last post we study that in Lightning we have two types of Events. We study the Component Event. In this post we will continue and will cover the application event.

Application Events

Application events follow a traditional publish-subscribe model. An application event is fired from an instance of a component. All components that provide a handler for the event are notified.

Here is the sequence of application event propagation.

  1. Event fired—An application event is fired. The component that fires the event is known as the source component.
  2. Capture phase—The framework executes the capture phase from the application root to the source component until all components are traversed. Any handling event can stop propagation by calling stopPropagation() on the event.
  3. Bubble phase—The framework executes the bubble phase from the source component to the application root until all components are traversed or stopPropagation() is called.
  4. Default phase—The framework executes the default phase from the root node unless preventDefault() was called in the capture or bubble phases. If the event’s propagation wasn’t stopped in a previous phase, the root node defaults to the application root. If the event’s propagation was stopped in a previous phase, the root node is set to the component whose handler invoked event.stopPropagation().

Create Custom Application Events

We use type=”APPLICATION” in the <aura:event> tag for an application event. Events can contain attributes that can be setbefore the event is fired and read when the event is handled.

For example, this c:appEvent application event has one attribute with a name of message.


<!--c:appEvent-->

<aura:event type="APPLICATION">

<!-- Add aura:attribute tags to define event shape. One sample attribute here. --> <aura:attribute name="message" type="String"/>

</aura:event>

The component that fires an event can set the event’s data. To set the attribute values, call event.setParam() or event.setParams(). A parameter name set in the event must match the name attribute of an <aura:attribute> in the event. For example, if you fire c:appEvent, you could use:

event.setParam(“message”, “event message here”);

The component that handles an event can retrieve the event data. To retrieve the attribute in this event, call event.getParam(“message”) in the handler’s client-side controller. This procedure of set and get attribute value in Event is same in both type of events.

Fire Application Events

An application event is fired from an instance of a component. All components that provide a handler for the event are notified.

Register an Event

A component registers that it may fire an application event by using <aura:registerEvent> in its markup. The name attribute is required but not used for application events. The name attribute is only relevant for component events. This example uses name=”appEvent” but the value isn’t used anywhere.


<aura:registerEvent name="appEvent" type="c:appEvent"/>

Fire an Event

Use $A.get(“e.myNamespace:myAppEvent”) in JavaScript to get an instance of the myAppEvent event in the myNamespace namespace.

Note: The syntax to get an instance of an application event is different than the syntax to get a component event, which is cmp.getEvent(“evtName“). Use fire() to fire the event.


var appEvent = $A.get("e.c:appEvent");
// Optional: set some data for the event (also known as event shape)  A parameter’s name must match the name attribute of one of the event’s <aura:attribute> tags
//appEvent.setParams({ "myParam" : myValue });

appEvent.fire();

Handling Application Events

Use <aura:handler> in the markup of the handler component.

For example:


<aura:handler event="c:appEvent" action="{!c.handleApplicationEvent}"/>

The event attribute specifies the event being handled. The format is namespace:eventName. The action attribute of <aura:handler> sets the client-side controller action to handle the event.

Note: The handler for an application event won’t work if you set the name attribute in <aura:handler>. Use the name attribute only when you’re handling component events. In this example, when the event is fired, the handleApplicationEvent client-side controller action is called.

Firing Lightning Events from Non-Lightning Code

We can fire Lightning events from JavaScript code outside a Lightning app. For example, our Lightning app might need to call out to some non-Lightning code, and then have that code communicate back to our Lightning app once it’s done.

For example, we could call external code that needs to log into another system and return some data to our Lightning app. Let’s call this event mynamespace:externalEvent. We’ll fire this event when yur non-Lightning code is done by including this JavaScript in our non-Lightning code.We can use this process to call third part APi and can do authentication calls.


var myExternalEvent;

if(window.opener.$A && (myExternalEvent = window.opener.$A.get("e.mynamespace:externalEvent"))) { myExternalEvent.setParams({isOauthed:true});

myExternalEvent.fire();

}

Communicating with Events Firing Lightning Events from Non-Lightning Code window.opener.$A.get() references the master window where your Lightning app is loaded.

Do you have anything to add please let me know in comments section. Happy Programming 🙂

Lightning Events: Detail Overview

Lightning Events: Detail Overview

In my previous post I have explain the Lightning Basic. But I in that post we only cover basics of Lightning events. I think Lightning events are more complex and need a detailed separate post. So today we will cover lightning events in details.

Events are used in Lightning to communicate between two components and pass data between them. In Lightning we have two types of Events:

  1. Component Event
  2. Application Event

Component Event: A component event is fired from an instance of a component. A component event can be handled by the component that fired the event or by a component in the containment hierarchy that receives the event.

Here’s the sequence of component event propagation.

  1. Event fired—A component event is fired.
  2. Capture phase—the framework executes the capture phase from the application root to the source component until all components are traversed. Any handling event can stop propagation by calling stopPropagation() on the event.
  3. Bubble phase—The framework executes the bubble phase from the source component to the application root until all components are traversed or stopPropagation() is called.

Create Custom Component Events

We can create a custom component event with file .evt. Events can contain attributes that can be set before the event is fired and read when the event is handled. Use type=”COMPONENT” in the tag for a component event. For example, this c:compEvent component event has one attribute with a name of message.


<!--c:compEvent-->

<aura:event type="COMPONENT">

<!-- Add aura:attribute tags to define event shape. One sample attribute here. --> <aura:attribute name="message" type="String"/>

</aura:event>

Fire Component Events Fire We can fire the Component event to pass date to other component. A component event can be handled by the component that fired the event or by a component in the containment hierarchy that receives the event.

Register an Event A Component first register a event which he will fire. Value of the name attribute is used for firing and handling events. Fire an Event To get a reference to a component event in JavaScript, we can use cmp.getEvent(“evtName”) where evtName matches the name attribute . And we need to  Use fire() to fire the event from an instance of a component. For example, This is similar as action attribute in apex:actionfunction.


var compEvent = cmp.getEvent("sampleComponentEvent");

// Optional: We can also set the data in attribute which we need to pass

// A parameter’s name must match the name attribute of one of the event’s tags

// compEvent.setParams({"myParam" : myValue });

compEvent.fire();

Handling Component Events

A component event can be handled by the component that fired the event or by a component in the containment hierarchy that receives the event. For example: The name attribute in must match the name attribute in the tag in the component that fires the event. The action attribute of sets the client-side controller action to handle the event. The event attribute specifies the event being handled. The format is namespace:eventName


<aura:handler name="sampleComponentEvent" event="c:compEvent"
action="{!c.handleComponentEvent}"/>

In my next post we will cover the Application event and some difference between these two.

Do you have anything to add please let me know in comments section. Happy Programming 🙂

Salesforce Spring 18: Quick Preview

Salesforce Spring 18: Quick Preview

Happy New Year Everyone. And god bless to them who had hardcoded 2017 in there code. Salesforce has just release Spring 18 and this time also the main focus remains n Einstein and Lightning. In this release there are many good feature are introduced. So let’s take a quick look at them.

spring-18-logo-268x300
Spring 18
  1. Gather Feedback with Salesforce Surveys (Generally Available)

Salesforce now has introduced Surveys. Create beautiful, easy-to-use forms for collecting feedback and data from your users or customers. Add different types of questions to gather the data you need. All your valuable survey data is stored in your org, so you can harness the power of Salesforce to view data, create reports and dashboards, and share insights with your company.

  1. Enable the New URL Format for Lightning Experience and the Salesforce

Salesforce is changing the URL format used by Lightning Experience standard apps and the Salesforce mobile app. The new URL format is more readable and addresses the issue of being directed to an unexpected location when accessing Lightning Experience URLs before authenticating. This update doesn’t apply to console apps and communities.

For example:

Custom or installed Lightning components that parse a URL may break or behave differently after this change, especially components with logic that relies on window.location or the aura:locationChange event.

  1. Einstein

Einstein Opportunity Scoring: Artificial Intelligence for Opportunities (Generally Available)

Salesforce has expanded the Sales Cloud Einstein product suite with Einstein Opportunity Scoring. Now we can prioritize our way to more business. This feature is new in Lightning Experience and Salesforce Classic.

Einstein Forecasting: Intelligent Predictions About our Sales Teams (Beta)

Let Einstein take the guesswork out of forecasting. Use artificial intelligence to get more certainty and visibility into how your sales teams are doing. This feature is new in both Lightning Experience and Salesforce Classic.

Einstein Lead Scoring: Clearer Predictive Factors and Detection of Rank and Department

Get a clearer view of the reasons behind each score. And see better scores with rank and department information. These changes apply to both Lightning Experience and Salesforce Classic.

Einstein Insights: Access Account and Opportunity Insights from Your Mobile Device (Pilot)

Einstein Insights are now. See predictions about which deals are likely to be won, reminders to follow up, and notifications when key moments in a deal take place. Reps can take action directly from the insights, including editing the related record or sending emails to relevant contacts. This change applies to Salesforce for iOS and Salesforce for Android.

Einstein Platform Services APIs

Use the Einstein Platform Services APIs to easily AI-enable your apps with image recognition and natural language processing. Using the Einstein Vision APIs, you can leverage pre-trained classifiers or train your own custom classifiers to solve a vast array of specialized image recognition use cases. With the Einstein Language APIs, you can harness the power of natural language processing to analyze text and infer the sentiment or intent behind that text. Make your apps smarter, and look like a genius at the same time.

  1. Stay on Top of Duplicate Records by Using Duplicate Jobs PERMISSIONS

Good clean data builds the trust of your sales team. It also helps you work toward complying with various data protection and privacy regulations. So you’ve got everything to gain by getting a global view of duplicate records. Use duplicate jobs with standard or custom matching rules to scan your Salesforce business or person accounts, contacts, or leads for duplicates. Share job results with others and merge the duplicates—all within Salesforce. Use information about duplicate jobs we’ve run to track the progress in reducing duplicate records. This feature is new in Lightning Experience.

  1. Salesforce IoT: IoT REST API, New Context UI, IoT Usage Monitoring, and Email Alerts

Use Salesforce IoT Explorer Edition to process events from connected devices in near real time and create meaningful interactions with your customers. Explorer offers built-in integration with Salesforce data and lets you leverage your Customer Relationship Management (CRM) investments in Salesforce. This release includes a new IoT REST API to retrieve and manipulate orchestrations and their related components, and a new user interface for contexts. Also, Salesforce is introducing an IoT usage monitoring page, email alerts, and the ability to export and delete IoT data for data privacy.

  1. Use without sharing for @AuraEnabled Apex Controllers with Implicit Sharing (Critical Update)

This critical update changes the behavior of @AuraEnabled Apex controllers that don’t specify with sharing or without sharing to default to without sharing. This change relaxes the security for controllers that don’t explicitly set sharing behavior using these keywords. It’s a best practice to always use with sharing in Apex controllers used with Lightning components.

  1. Find Dynamically Created Components

We can now use cmp.find() to find a component that we have create dynamically with $A.createComponent().

  1. Remove Instance Names from URLs for Visualforce, Community Builder, Site.com Studio, and Content Files (Critical Update)

Salesforce is stabilizing the hostname of Visualforce, Community Builder, Site.com Studio, and content file URLs by removing instance names from URLs. This critical update applies to orgs that have a deployed My Domain. It will be activated automatically on March 16, 2019.

Prior to Winter ’18, Visualforce, Community Builder, Site.com Studio, and content file URLs included the instance name of the org, even when a My Domain name was deployed. An instance name identifies where your Salesforce org is hosted. When Salesforce remove the instance name from our URLs, hostnames change.

Here are two examples:

  • mydomain–c.visualforce.com replaces mydomain–c.na1.visual.force.com
  • mydomain–c.documentforce.com replaces mydomain–c.na1.content.force.com

Note: All *.content.force.com URLs will be replaced with *.documentforce.com.

  1. Employ Validation Rule Formulas with Custom Metadata Types

If we use validation rules, you know how helpful they can be, but you have to hard code some of the values. Now you can store values you previously had to hard code in custom metadata types and reference them in your validation rules.

Example: Use a validation rule formula to limit discounts on certain brands to 10%. You can use validation rules without custom metadata. However, to change an amount, you must update the hardcoded value. If you have multiple validation rules that check the discount amount, you have to manage all of them. Instead, use a validation rule formula with custom metadata types.

  1. Create a custom metadata type. In this example, we named it DiscountLimits.
  2. Create a custom field for your type. We named it maxDiscount.
  3. Create a record. We named it FoodDiscount.

When done, you can reference the custom metadata type in your validation rule. The syntax is:

$CustomMetadata.CustomMetadataTypeAPIName.RecordAPIName.FieldAPIName

Make sure to use the correct suffixes. For the custom metadata type, use __mdt. For fields, use __c. Records require no suffix.

Your validation rule for this example looks something like this:

Discount > $CustomMetadata.DiscountLimits__mdt.FoodDiscount.maxDiscount__c

  1. In Spring 18 Salesforce added two new methods in list class. They are

contains(listElement)

Returns true if the list contains the specified element.

indexOf(listElement)

Returns the index of the first occurrence of the specified element in this list. If this list does not contain the element, returns -1.

So now we no longer dependent on set to check for duplicate key at the same time we can maintain order as well.

  1. Collect Debug Logs for Guest Users Without Setting Cookies

In Winter ’17, Salesforce introduced a change that required guest users to set browser cookies to activate debug logging. people didn’t like the Winter ’17 change. Setting cookies is complicated, and the change made collecting debug logs for public users’ asynchronous activity impossible. Now public users no longer need a debug_logs browser cookie to trigger logging. This change applies to both Lightning Experience and Salesforce Classic.

  1. Avoid Huge Debug Logs for Managed Package Code

Debug logs no longer include ENTERING_MANAGED_PKG events when your log level for the Apex Code category is INFO or DEBUG. To log ENTERING_MANAGED_PKG events, set your Apex Code log level to FINE, FINER, or FINEST.

Einstein Object Detection: A Quick Overview

Einstein Object Detection: A Quick Overview

Einstein is now a known tech in market and Salesforce now making its library public from the beta mode. Recently Salesforce made Object Detection public available. Object Detection tells you where one or more objects are in an image which allows you to get specific counts and locations of objects within images.

For Object Detection first you need to train the Dataset. The process of dataset creation is bit different then the Einstein Vision.

To train an Einstein Object Detection model, you must provide the coordinates of the bounding boxes around the objects you want detected.  Draw a box around the object you want detected in the image, this is called a bounding box. For training you need the x/y pixel coordinates of the top left edge of that box, as well as the pixel width and height. You can have multiple objects within a single example image.

Once you’ve collected all of your example images and object data, you create a file with the name annotations.csv. This file is a key part of the Einstein Object Detection training. It tells the Einstein Platform where in your sample data it will find the objects it needs to pay attention to during training.

OD1.png

This is how the sample file will look. For the demo purpose I have used the same file used in Einstein Site. After the dataset is created you need to train It. This process is same as Einstein Vision. And once the dataset is trained you are ready to make your first request  find the object details.

First we need to create the dataset.

HttpRequest req = new HttpRequest();
 req.setMethod('POST');
 req.setEndpoint('https://api.einstein.ai/v2/vision/datasets/upload');
 req.setHeader('content-type', 'multipart/form-data; charset="UTF-8"; boundary="1ff13444ed8140c7a32fc4e6451aa76d"');
 req.setHeader('Authorization', 'Bearer <TOKEN>');

 req.setHeader('Cache-Control', 'no-cache');
 // Compose the form
 string form64 = '';

form64 += HttpFormBuilder.WriteBoundary();
 form64 += HttpFormBuilder.WriteBodyParameter('path', 'https://einstein.ai/images/alpine.zip');
 form64 += HttpFormBuilder.WriteBoundary();
 form64 += HttpFormBuilder.WriteBodyParameter('type', 'image-detection');
 form64 += HttpFormBuilder.WriteBoundary(HttpFormBuilder.EndingType.CrLf);

blob formBlob = EncodingUtil.base64Decode(form64);
 string contentLength = string.valueOf(formBlob.size());

 req.setBodyAsBlob(formBlob);
 req.setHeader('Connection', 'keep-alive');
 req.setHeader('Content-Length', contentLength);
 req.setTimeout(60*1000);

Http h = new Http();
 String resp;
 HttpResponse res = h.send(req);
 resp = res.getBody();
 system.debug(resp);
 Map<String,object> responseMap =(Map<String,object>)JSON.deserializeUntyped(res.getBody()) ;

As this is a Asynchronous process so we need to check the status of dataset creation. For this we need to make another request

HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint('https://api.einstein.ai/v2/vision/datasets/&amp;amp;amp;amp;amp;amp;lt;DATASET ID&amp;amp;amp;amp;amp;amp;gt;');
req.setHeader('Authorization', 'Bearer <TOKEN>');
req.setTimeout(60*1000);

Http h = new Http();
String resp;
HttpResponse res = h.send(req);
resp = res.getBody();
system.debug(resp);

Now after our dataset is created we need to train the dataset.

HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setEndpoint('https://api.einstein.ai/v2/vision/train');
req.setHeader('content-type', 'multipart/form-data; charset="UTF-8"; boundary="1ff13444ed8140c7a32fc4e6451aa76d"');
req.setHeader('Authorization', 'Bearer <TOKEN>');

req.setHeader('Cache-Control', 'no-cache');
// Compose the form
string form64 = '';

form64 += HttpFormBuilder.WriteBoundary();
form64 += HttpFormBuilder.WriteBodyParameter('name','Alpine Boxes on Shelves');
form64 += HttpFormBuilder.WriteBoundary();
form64 += HttpFormBuilder.WriteBodyParameter('datasetId', '<DATASET ID>');
form64 += HttpFormBuilder.WriteBoundary(HttpFormBuilder.EndingType.CrLf);

blob formBlob = EncodingUtil.base64Decode(form64);
string contentLength = string.valueOf(formBlob.size());

req.setBodyAsBlob(formBlob);
req.setHeader('Connection', 'keep-alive');
req.setHeader('Content-Length', contentLength);
req.setTimeout(60*1000);

Http h = new Http();
String resp;
HttpResponse res = h.send(req);
resp = res.getBody();
system.debug(resp);

As this is also asynchronous process we we need to check the status.

HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setEndpoint('https://api.einstein.ai/v2/vision/train/&amp;amp;amp;lt;MODEL ID&amp;amp;amp;gt;');
req.setHeader('Authorization', 'Bearer <TOKEN>');
req.setTimeout(60*1000);

Http h = new Http();
String resp;
HttpResponse res = h.send(req);
resp = res.getBody();
system.debug(resp);
</pre>
Now once our Model training is completed we are ready to make our request. For testing purpose we will use this image.&nbsp;<img class="alignnone size-full wp-image-664" src="https://newstechnologystuff.files.wordpress.com/2017/12/alpine.jpg?w=680" data-wpmedia-src="https://newstechnologystuff.files.wordpress.com/2017/12/alpine.jpg" alt="alpine" width="4032" height="3024" data-mce-src="https://newstechnologystuff.files.wordpress.com/2017/12/alpine.jpg?w=680">
<pre>HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setEndpoint('https://api.einstein.ai/v2/vision/detect');
req.setHeader('content-type', 'multipart/form-data; charset="UTF-8"; boundary="1ff13444ed8140c7a32fc4e6451aa76d"');
req.setHeader('Authorization', 'Bearer <TOKEN>');
req.setHeader('Cache-Control', 'no-cache');
// Compose the form
string form64 = '';
form64 += HttpFormBuilder.WriteBoundary();
form64 += HttpFormBuilder.WriteBodyParameter('sampleLocation','http://einstein.ai/image/alpine.jpg');
form64 += HttpFormBuilder.WriteBoundary();
form64 += HttpFormBuilder.WriteBodyParameter('modelId', '&amp;lt;MODEL ID&amp;gt;');
form64 += HttpFormBuilder.WriteBoundary(HttpFormBuilder.EndingType.CrLf);
blob formBlob = EncodingUtil.base64Decode(form64);
string contentLength = string.valueOf(formBlob.size());
req.setBodyAsBlob(formBlob);
req.setHeader('Content-Length', contentLength);
req.setTimeout(60*1000);
Http h = new Http();
String resp;
HttpResponse res = h.send(req);
resp = res.getBody();
system.debug(resp);

The result we get is

{{ "probabilities": [ { "resultType": "DetectionResult", "label": "Alpine - Oat Cereal", "probability": 0.9947397, "boundingBox": { "minX": 2112, "minY": 916, "maxX": 2880, "maxY": 1933 } }, { "resultType": "DetectionResult", "label": "Alpine - Corn Flakes", "probability": 0.9945168, "boundingBox": { "minX": 791, "minY": 915, "maxX": 1540, "maxY": 1853 } }, { "resultType": "DetectionResult", "label": "Alpine - Bran Cereal", "probability": 0.9940328, "boundingBox": { "minX": 2873, "minY": 848, "maxX": 3629, "maxY": 1945 } }, { "resultType": "DetectionResult", "label": "Alpine - Bran Cereal", "probability": 0.9933943, "boundingBox": { "minX": 1433, "minY": 918, "maxX": 2156, "maxY": 1957 } } ]}

So you can check we got the correct result with 99+ probability. So this is very important feature of lightning which one can use in real life in sore to detect inventory, Parts etc.

So tell me in comments section what did you like most about Einstein. Happy Programming 🙂

Salesforce Einstein Intent: A Quick Overview

Salesforce Einstein Intent: A Quick Overview

Hi All, As Dreamforce is over and there are lots of new things in the market. but Einstein is on top of that. In my previous post I have already shared about Einstein Vision to predict the image. Today we will cover the basic of Einstein Intent A search Prediction.

You can use Einstein Intent to make a case prediction and route the case to different user  so that case can be solved much faster with less human intervention.

3342fe7-intent_and_sentiment_flow.png

The process is same as we have done for Einstein Vision. First if you don’t have API key or access token then you need to create one. You can follow the same steps as previous post.
After that we first need to create sample Dataset. In this step, you define the labels that you want the model to output when text is sent into the model for prediction. Then you gather text data for each of those labels, and that text is used to create a model.

You can use sample CSV which I have used or can create your own as well.

Then you need to train that dataset. You can also make request to check status of training. and once training is completed you can make your first prediction.

The text which I used for prediction is “Why my shipping address is changed.” And the response which I get is

Prediction Result.png

 

Its related to Billing and it is Correct. You can create large dataset to make these prediction more accurate. And can play with it yourself.

Let me know what you like most about Einstein in comments. If you want to add something share with me in comments section.

Happy Programming 🙂