Monday, December 25, 2017

Custom Permissions : Way to control user access to different functionality in Salesforce

Problem Statement

There is a very common scenario in which admin has to configure different validation rules, workflow rules which fires based on User Id or Profile Id.

Consider below mentioned scenarios:
  • Position (Custom Object) records can be closed by user belonging to specific profile.
  • You have created a VF page where you display specific section to users based on profile. 

In both scenarios, you have to check for profile Id in validation rule as well as in VF (to rendered specific section).





Now after some time if you have to enable the above mentioned functionality to new profile or to specific user then, you have to modify the validation rule as well as your VF page code.

Solution

You can use custom permission for these kind of scenarios.

Custom permission is just a record which can be referenced in validation rule, workflow rules, VF, Apex etc. You can assign custom permission to profile or permission set (similar way in which we assign VF page and Apex class).

Now you can check custom permission in validation rules and VF by using global $Permission variable.

Steps to use custom permission and use as per above scenario:
  • Create custom permission (Navigate to Set Up--> Develop--> Custom Permissions).
  • Create new custom permission.

  • Add custom permission to profile or permission set(navigate to Enable custom permission section).

  • Modify the validation rule to and VF to refer custom permission instead of profile id.



So custom permission help us to control different functionality in salesforce to different users. If you have to enable functionality for single user then add custom permission to permission set and then add that permission set to user.

Alternate Approach

All admins/developers were using custom setting to control functionality for different users but after launch of custom permission, jobs of developer or admin will be easy.

Custom Permission in Apex

You can use below static method to find out list of user who have permission for custom permission:


public static List<User> findUsersWithCustomPermission(String customPermissionname)
{
    List<user> userList = new List<User>();
Set<Id> permissionSetIds = new Set<Id>();
    for (SetupEntityAccess access : [ SELECT ParentId FROM SetupEntityAccess 
WHERE SetupEntityId IN ( SELECT Id FROM CustomPermission 
            WHERE DeveloperName = :name)]) {
        permissionSetIds.add(access.ParentId);
    }
if(permissionSetIds.size()>0){
userList = [SELECT Username FROM User 
WHERE Id IN (SELECT AssigneeId FROM PermissionSetAssignment
WHERE PermissionSetId IN :permissionSetIds)];
}
    return userList;
}

Hope this will help!!!

Saturday, December 16, 2017

Insufficient Privileges : How to troubleshoot record access issue in Salesforce

"Insufficient Privileges" is common error which appears on user interface if user tries to access/edit a record. As we know that apart from OWD and profile, record can be shared by using sharing rules, role hierarchy, manual sharing or apex sharing.



If some user report this kind of issue, then instead of checking all sharing options, we can directly run below query to check what kind of access that user have for given record.

SELECT RecordId, HasReadAccess, HasTransferAccess, MaxAccessLevel, HasAllAccess, HasDeleteAccess, HasEditAccess FROM UserRecordAccess  WHERE UserId = “005xxxxxxxxx”AND RecordId = “001xxxxxxxx”

Where 005xxxxxxxxx is user id and  001xxxxxxxx is record Id.

HasAllAccess Indicates whether a user has all access–read, edit, delete, and transfer—to the record (true) or not (false).
HasReadAccess, HasEditAccess , HasDeleteAccess ,HasTransferAccess return Boolean value.
MaxAccessLevel return access level like None, read, Edit,Delete,Transfer and All.


This query will help to understand whether user has access to record or not. After that you can check different options (sharing rules, role, apex sharing etc.) to find out why that user is not having access to record.

Hope this will help!!!

Tuesday, December 12, 2017

Formatting and Localizing dates in javascript in Lightning

Date formatting in javascript

AuraLocalizationService JavaScript API provides methods for localizing dates and formatting.

Use the formatDate() to format date by providing format string as second argument.

formatDate (String | Number | Date date, String formatString)

Use $A.localizationService to use the methods in AuraLocalizationService.

Below are few examples of format string and their output:

var now = new Date();
var dateString = "2017-12-12";

// Returns date in the format "Dec 12, 2017"
console.log($A.localizationService.formatDate(dateString));

// Returns date in the format "2017 12 12"
console.log($A.localizationService.formatDate(dateString, "YYYY MM DD"));

// Returns date in the format "December 12 2017, 11:03:47 PM"
console.log($A.localizationService.formatDate(now, "MMMM DD YYYY, hh:mm:ss a"));

// Returns date in the format "Dec 12 2017, 11:03:57 PM"
console.log($A.localizationService.formatDate(now, "MMM DD YYYY, hh:mm:ss a"));

Refer below link for more details:
tokens supported in format string

Localization

Lightning framework provides client-side localization support on input/output elements. This helps in customize the date and time format.

For example, below code will display date in Europe/Berlin timezone (Dec 13, 2017 2:17:08 AM)

<aura:component>
    <ui:outputDateTime value="2017-12-13T02:17:08.997Z"  timezone="Europe/Berlin" />
</aura:component>

To customize date and time format use, lightning:formattedDateTime.

The attributes on lightning:formattedDateTime helps you to control formatting at a granular level. For example, you need to display the date using the MM/DD/YYYY format, then use below code:

<lightning:formattedDateTime value="{!v.datetimeValue}" timeZone="Europe/Berlin" year="numeric" month="numeric" day="numeric"/>


datetimeValue can be set in controller code either in doInit or any other function as mentioned below:

var date = new Date();
component.set("v.datetimeValue", date)

Locale Information on client side controller

  • $A.get("$Locale.timezone")  : This will return timezone like Europe/Paris, Europe/Berlin etc.
  • $A.get("$Locale.currency") : This will return org currency locale like "$", "¥" etc.

Hope this will help!!!!

Tuesday, November 7, 2017

Access Grants : Record Access Calculation in Salesforce

Salesforce always check record accessibility whenever user view record in order to maintain data visibility as per sharing settings specified in org. Record access can be granted based on role hierarchy, sharing rules, in-built sharing etc.

Imagine a large scale implementation which involve large number of roles, sharing rules and customized apex managed sharing. In order to determine user access for record by considering  this scenario will take lot of time and will result in performance issue.

In order to avoid this kind of performance issues, Salesforce doesn't calculate record access in real time but it calculate record access whenever any configuration change occurs and store that information. This helps in calculating the record access in real time instead of traversing all hierarchies, applying all sharing rules or analyzing other available record sharing mechanism.

Access Grants

Salesforce uses Access Grants in order to identify how much access user have to object records. This comes into picture only when OWD for object is either PRIVATE or PUBLIC READ ONLY.
Access Grant also specify the sharing tool (like sharing rule, teams,roles etc) responsible for providing record access to user.

There are four types of access grants used by Salesforce:
  • Explicit Grants
Salesforce uses explicit grants when records are shared directly to user or groups. Below are few scenarios which are considered under this:
    1.  Teams : When user is part of account, sales or case team.
    2. When user or queue becomes record owner. All users under that queue get access to record.
    3. When record is shared by using sharing rule, apex manage sharing and manual sharing.
    4. When record is shared with user or queue based on assignment rule or territory rules share record with territory. All users under that territory get access to record.
Note: If you are using Enterprise Territory management, user can not share record manually to territory and while using sharing rules or apex managed sharing, record can not be shared with territory groups.
  • Implicit Grants
This is called as built-in sharing in salesforce. These are predefined record sharing behavior for  Sales, Service and portal applications. For example:
    1. User can view parent account record if they have access to child opportunity, case or contact record.
    2. If user has access to parent account, then they can access child opportunities, cases and contact records.
  • Group Membership Grants
This grant comes into picture when user, personal or public group, queue, role or territory is a member of group that has explicit access to record. So it means all user users or group which are part of public group with which any record is shared, the all group members and members present in sub-group  will get access to records.
  • Inherited Grants
This comes into picture when user get access to records because of role hierarchy, territory hierarchy or inherit access through group hierarchy.


Sharing database Architecture

Salesforce store access grants in three types of tables:
  • Object Record Tables
This table indicate which user, group or queue own each record.
  • Object Sharing Tables
This table store information like which record is shared with which user or group either by apex manage sharing rule or declarative sharing when object OWD is not PUBLIC READ/WRITE.

This table store data that supports explicit and implicit grants.
  • Group Maintenance Tables
This table store data that supports group membership grants and inherited grants. 




Monday, November 6, 2017

Declarative Sharing Vs Apex Managed Sharing

Force.com platform provides declarative sharing option which are easy to configure and meets basic data sharing requirements.

Different declarative sharing options

  • Record Ownership
Record owner will have read, edit, delete, share and transfer access.
  • Teams
Account teams, sales team and case team allow group of users to have access to different account, opportunities and case records.
  • Role hierarchy
This allow users in higher role to have same level of access to data which users have in lower hierarchy.
  • Sharing rules
You can create owner based or criteria based sharing rules in system which will open up the record access to users. Records can be shared to public group, roles,roles and internal subordinates, roles internal and portal subordinates etc.

Sharing rules also allow to share data with external users. The default external access level must be more restrictive or equal to default internal access level.

For example, if external sharing default is set to Public Read-Only for custom object, then valid internal default settings will be Private and Public Read Only.
  • Manual Sharing
Manual Sharing is also known as User Managed Sharing. This option allow user to share record manually with another user or public group, portal roles, portal users, roles, roles and internal subordinates, roles internal and portal subordinates etc.

Remember if record owner share record manually with another user and later on record owner changes, then manual sharing record will be deleted.

Apex Managed Sharing

This allows developers to share records by using apex code. This is also known as "Programmatic Sharing". Object share records can be created for standard and custom object but custom sharing reason can be defined for custom object only.

For example, if we have to share a record with a user specified in lookup field, then you can use trigger to create record in share table to provide access. 
Valid access level for records are Edit/ Read.

If OWD for object is either Public Read only or Private, then share table will exist for that object. You can create record in that share table to provide access to any user. Below are 4 fields you need to specify in order to create record in share table:
  • ParentId : 15 digit or 18 digit id of record which you want to share.
  • UserOrGreoupId : User id or group Id
  • Access Level : Edit or Read. This field must be set to an access level that is higher than the organization’s default access level for the parent object.
  • RowCause : You can specify the reason for sharing record. First define sharing reason for custom object. For standard object, you can specify Manual.
 You can not update ParentId field.

You can share record with same user or group by using different sharing reasons.

Consideration while using Apex Managed Sharing
  • If record owner changes, then sharing created through apex managed sharing (if row cause is not manual and uses custom sharing reasons) are maintained but if user share record manually, then record sharing will be lost if owner changes.
  • User with "modify All Data" can only add, edit or delete records in share table.

Tuesday, October 31, 2017

Change Set Implementation Tips

Change sets allows to deploy metadata between connected orgs. By using this, Admin can create the change sets and add the components to it and can deploy it to next org. It can be used to deploy metadata between sandboxes and from sandbox to production.

Below diagram explain the change set implementation:


 Main challenge with change sets is mentioned below.

  • If any error comes while validation using change sets then it stops validating other components. So we have fix the error and then again needs to validate it. But if we migrate metadata using ANT, then we get all deployment errors which help us to fix all errors in one shot.
  • Deployment process using change sets becomes complex if it involves large number of different metadata.

Solution

If we split the change set into different change set, then it will make job easier. You can split components in different change set and deploy it in order mentioned below:
  • First Change set
    • Static Resource
    • Custom Data Types
    • Custom Fields
    • Custom Labels
    • Custom Objects
    • Folders
    • Record Types
    • Validation Rules
    • Custom Settings
    • Field Sets
    • Remote Sites
    • Documents
  • Second Change set
    • Custom Report Types
    • List Views
    • Apex Classes
    • Apex Sharing Reasons
    • Apex Triggers
    • VisualForce Components
    • VisualForce Pages
    • Email Templates
    • S-Controls
    • Letterheads
    • Aura Components
  • Third Change set
    • Reports
    • Dashboards
  • Fourth Change set
    • Page Layouts
    • Buttons
    • Links
    • Homepage Components
    • Homepage Layouts
  • Fifth Change set
    • Workflow Email Alerts
    • Workflow Field Updates
    • Workflow outbound Messages
    • Workflow Rules
    • Workflow Tasks
  • Sixth Change set
    • Apps
    • Tabs
    • Analytic Snapshots
    • Language Translations

Hope this will help!!

Monday, October 30, 2017

Lightning Tree : Generic Lightning Component to Display Hierarchy for any sObject

In API version 41.0, Salesforce has introduce new component called lightning:tree. This can be used to display hierarchy in tree structure. Before this, we have to used jquery or other javascript libraries to implement this.

After introduction of lightning:tree, it is very easy to use this and implement this in short time.

I have created a generic lightning component to display records hierarchy in tree structure. You just have specify below attributes to component to display tree structure:

  • ltngcurrentRecId : Salesforce 15 or 18 digit Id of any record for which you want to see hierarchy.
  • ltngSobjectname : sObject API name (like Account, Case, Position__c etc.)
  • ltngParentFieldAPIName : Field API name which creates self relationship between records.
  • ltngLabelFieldAPIName : Field API name which will be used to display records in tree structure(like Name, CaseNumber, Title__c etc.).
  • ltngHierarchyHeader : Header label which will be displayed above tree structure.


Below is complete complete code for your reference:


You can use this code as reference and can implement different logic.

Hope this will help!!!

Looking forward for everyone's comments and suggestions.

Sunday, October 15, 2017

How to fix error while deploying new custom objects along with list views to new Org

Below are the two different errors we got when we deploy new custom object along with list view to new org through single single package:


  • Error: This View Unique Name already exists or has been previously used. Please choose a different name
First error happens if you have list view on object which is shared with all internal users. If you remove below tags from exported metadata of list view, then you will not get this error.


<sharedTo>
        <allInternalUsers></allInternalUsers>
</sharedTo>

After removing these tags, I was able to deploy successfully.

  • Added entity: New_Object__c support on queue: New_Queue
This error happens because your list view refers to queue and same queue refer to same object.

Resolutions:
  • Split your package for deployment because there is dependency between object, list view and queues. Deploy these three things separately. 
  • First deploy custom object with basic information. Remove list views tags from .object file before deployment.
Best Solution:

Create custom object manually in target org and deploy the package. This is best approach as you don't have to change any metadata files.

Recommendation:

Deployment errors also come if you deploy approval process which contains approver as queue and those quesues refer to same object on which you have approval process. So always first create custom object in target org and then deploy queues and then deploy remaining things.

Hope this will help!!!


Wednesday, October 4, 2017

Webinar : Build Smarter Apps with Einstein Platform Services

This webinar was held on 28th September, 2017.  Below are the list of speakers for this webinar:
  • SHANE MCLAUGHLIN (Principal Demo Engineer,Salesforce)
  • KATIE HURLEY (Senior Manager, Product Marketing Salesforce)
  • KRIS CHANT (Director, Developer Marketing Salesforce)
  • MICHAEL MACHADO (Director, Product Management Salesforce)

This webinar helps in understanding Einstein Platform Services. This is intended for Administrators and developers. If you missed this webinar then, you can refer below webinar recordings and slides.


Webinar Recording:



Webinar Slides: 

Saturday, September 30, 2017

Lightning Data Services : Hands on Training Guide

In this blog, we are going to create a sample app which will list 10 recents accounts from Salesforce and will view and edit account using Lightning Data Services.

So lets start.

Create a “LDSAccountListViewApp” Lightning Application

  • In the Developer Console, choose File > New > Lightning Application.
  • Specify “LDSAccountListViewApp” as the app name and click submit.
  • Specify below code in lightning app.

<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
</aura:application>

  • Save the file.
  • Click  preview

Create a LDSAccountEdit lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “LDSAccountEdit” as the component name and click submit. Paste below code and Save the file.
<aura:component > 
<aura:attribute name="recordId" type="String" required="true"/> 
<aura:attribute name="recordInfo" type="Object"/> 
<aura:attribute name="fieldsInfo" type="Object"/> 
<aura:attribute name="recordError" type="String"/> 
<aura:attribute name="currView" type="String" />
<force:recordData aura:id="recordLoader" 
recordId="{!v.recordId}" 
mode="EDIT" 
targetRecord="{!v.recordInfo}" 
targetFields="{!v.fieldsInfo}" fields="Name,Owner.Name,AnnualRevenue,AccountNumber" 
targetError="{!v.recordError}" 
recordUpdated="{!c.handleRecordChanges}" /> 
<div class="maincontainer"> 
</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>

  • Click CONTROLLER in the right side bar of the code editor and replace code with below code and save the file.
({ 
    handleRecordChanges: function(component, event, helper) {
  var eventParams = event.getParams(); 
if(eventParams.changeType === "LOADED") { 
// record is loaded 
var fieldsDetails= component.get("v.fieldsInfo"); 
console.log("fieldsInfo is loaded successfully. TargetField"+ JSON.stringify(fieldsDetails)); 
var recordDetails= component.get("v.recordInfo"); 
console.log("recordInfo -Target Record"+ JSON.stringify(recordDetails)); 
console.log('Record loaded successfully'); 
     } 
})
  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountEdit recordId=“0017F000004R9C3QAK”/>
</aura:application>

Lets see what we have done!!!

  • In order to display more account fields, replace the code inside div with class “maincontainer” with below code.


<div class="maincontainer"> 
      <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
           <div class="slds-form-element__control" > 
                  <lightning:input label="Account Name" name="accname" value="{!v.fieldsInfo.Name}" /> 
           </div> 
       </div>
       <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
               <lightning:input type="number" label="Annual Revenue" name="atype" value="{!v.fieldsInfo.AnnualRevenue}"  formatter="currency"/>
             </div> 
        </div> 
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                  <lightning:input label="Account Number" name="accnum" value="{!v.fieldsInfo.AccountNumber}" /> 
            </div>
         </div>
          <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
                <div class="slds-form-element__control" > 
                       <lightning:button variant="brand" label="Save" onclick="{!c.saveRecordHandler}"/> 
                        <lightning:button variant="brand" label="Back" /> 
               </div> 
               </div>
 </div>


  • Click CONTROLLER in the right side bar of the code editor and add saveRecordHandler function and save the file.
saveRecordHandler: function(component, event, helper) {       component.find("recordLoader").saveRecord($A.getCallback(function(saveResult) { 
   if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {         console.log('Record updated successfully'); 
   }else if (saveResult.state === "ERROR") { 
      console.log('Problem error: ' + JSON.stringify(saveResult.error)); 
   } else { 
        console.log('Unknown problem, state: ' + saveResult.state + ', error: ' +        JSON.stringify(saveResult.error)); 
   } 
})); 
}
  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountEdit recordId=“0017F000004R9C3QAK”/>
</aura:application>

Lets see what we have done!!!


Create a LDSAccountView lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “LDSAccountView” as the component name and click submit.
  • Copy the code from “LDSAccountEdit” component and paste it here.
  • Add a attribute disabled="true" in all lightning:input tag
<lightning:input label="Account Name" name="accname" value="{!v.fieldsInfo.Name}" dsabled="true"/>
  • Remove "handleRecordChanges" attribute from <force:recorddata> tag.
  • Remove Lightning:button with label as "Save".
Complete code for LDSAccountView Component is :

<aura:component >
    <aura:attribute name="recordId" type="String" required="true"/>
    <aura:attribute name="recordInfo" type="Object"/>
    <aura:attribute name="fieldsInfo" type="Object"/>
    <aura:attribute name="currView" type="String" />

    <aura:attribute name="recordError" type="String"/>
    <force:recordData aura:id="recordLoader"  
                      recordId="{!v.recordId}"  
                      mode="VIEW"
                      targetRecord="{!v.recordInfo}"
                      targetFields="{!v.fieldsInfo}" 
                      fields="Name,Owner.Name,AnnualRevenue,AccountNumber"
                      targetError="{!v.recordError}"
                      />
    <div class="maincontainer"> 
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <lightning:input label="Account Name" name="accname" value="{!v.fieldsInfo.Name}" disabled="true"/> 
            </div> 
        </div>
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <lightning:input type="number" label="Annual Revenue" name="atype" value="{!v.fieldsInfo.AnnualRevenue}"  formatter="currency" disabled="true"/>
            </div> 
        </div> 
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <lightning:input label="Account Number" name="accnum" value="{!v.fieldsInfo.AccountNumber}" disabled="true"/> 
            </div>
        </div>
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <!--<lightning:button variant="brand" label="Save" onclick="{!c.saveRecordHandler}"/> -->
                <lightning:button variant="brand" label="Back" onclick="{!c.goBackToListView}"/> 
            </div> 
        </div>
    </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>

  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountView recordId=“0017F000004R9C3QAK”/>
</aura:application>

Lets see what we have done!!


Create a Apex class to fetch Account records
  • Create Apex Class “LDSAccountListViewController” to fetch latest 10 Account records from your org.
public with Sharing class LDSAccountListViewController { 
@AuraEnabled public static List<Account> findAccounts(){ 
List<Account> accList = new List<Account>(); 
accList=[select id, name,owner.name,type,AccountNumber,AnnualRevenue,Phone from Account order by lastModifiedDate DESC Limit 10]; 
return accList; 
}

Create a LDSAccountListView lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “LDSAccountListView” as the component name and click submit.
<aura:component controller="LDSAccountListViewController" > 
<aura:attribute name="accList" type="List" /> 
<aura:attribute name="menu" type="List" default="View,Edit"/>
<aura:attribute name="currentView" type="String" default="ListView"/>
<aura:attribute name="selectedRecord" type="String" /> 
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/> 
<!--Section for Account List View starts--> 
<aura:if isTrue="{!v.currentView =='ListView'}"> 
<div class="slds-card"> 
  <div class="slds-card__header slds-grid"> 
   <header class="slds-media slds-media_center slds-has-flexi-truncate"> 
         <div class="slds-media__figure"> 
      <lightning:Icon iconName="standard:account" size="large" variant="inverse" /> 
     </div> 
           <div class="slds-media__body"> 
     <h2> <span class="slds-text-heading_small">Accounts({!v.accList.length})</span> </h2>      
     </div> 
   </header> 
   <div class="slds-no-flex"> <lightning:button variant="brand" label="New Account" />   </div> 
    </div> 
    <div class="slds-card__body slds-card__body_inner"> 
        <aura:if isTrue="{!v.accList.length > 0}"> <!--display all accounts--> </aura:if> 
                                </div> 
          <footer class="slds-card__footer">@sunil02kumar</footer> </div> 
</aura:if>
<!--Section for Account List View ends--> 
</aura:component>

  • Click CONTROLLER in the right side bar of the code editor and add doInit function and save the file
({ 
    doInit : function(component, event, helper){ 
var action = component.get("c.findAccounts"); 
action.setCallback(this, function(response) { 
var state = response.getState(); 
if (state === "SUCCESS") { 
var apexResponse = response.getReturnValue();
//console.log('***apexResponse:'+ JSON.stringify(apexResponse));
component.set("v.accList", apexResponse);
console.log('********Accounts list view loaded successfully'); 
}else if(state === "ERROR"){ 
alert('Problem with connection. Please try again.'); 
}}); 
$A.enqueueAction(action); 
    } 
})
  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountListView />
</aura:application>

  • Update LDSAccountListView lightning Component. Replace <!--display all accounts-->  section with below code and save file.
<table class="slds-table slds-table_fixed-layout slds-table_bordered slds-table_cell-buffer">
<thead> 
      <tr class="slds-text-title--caps"> 
<th scope="col">Actions</th> 
<th scope="col">Name</th> 
<th scope="col">Account Number</th> 
</tr> 
</thead> 
<tbody> 
     <aura:iteration items="{!v.accList}" var="item"> 
     <tr class="slds-hint-parent"> 
  <td scope="row"> 
<lightning:buttonMenu iconName="utility:threedots" > 
  <aura:iteration items="{!v.menu}" var="menuItem"> 
                             <lightning:menuItem label="{!menuItem}" value="{!item.Id + '---' + menuItem}" onactive="{!c.onSelectMenuItem}"/> 
  </aura:iteration> 
</lightning:buttonMenu> 
</td> 
<td > {!item.Name}</td> 
<td > {!item.AccountNumber}</td> 
</tr> 
     </aura:iteration> 
</tbody> 
</table>
  • Click CONTROLLER in the right side bar of the code editor and add onSelectMenuItem function and save the file.
onSelectMenuItem : function(component, event, helper) { 
var selectedOption = event.getSource().get('v.value'); 
var selectedId = selectedOption.split('---')[0];
console.log('*************selectedId:'+selectedId);
component.set("v.selectedRecord",selectedId);
console.log('*************selectedOption:'+selectedOption); 
if (selectedOption.endsWith("View")) {
component.set("v.currentView","RecordView"); 
}else if(selectedOption.endsWith("Edit")){
component.set("v.currentView","RecordEdit"); 
}
  • Update LDSAccountListView lightning Component. Now we are going to display LDSAccountView and LDSAccountEdit components based selection made by user on menu item. Add below markup code in LDSAccountListView component after the section for account list views and save file.
<!--Section for Account record View starts--> 
<aura:if isTrue="{!v.currentView =='RecordView'}"> 
<c:LDSAccountView recordId="{!v.selectedRecord}" currView="{!v.currentView}"/> </aura:if> 
<!--Section for Account record View ends--> 

<!--Section for Account record edit starts--> 
<aura:if isTrue="{!v.currentView =='RecordEdit'}"> 
<c:LDSAccountEdit recordId="{!v.selectedRecord}" currView="{!v.currentView}" /> </aura:if> 
<!--Section for Account record edit ends-->

Update LDSAccountView & LDSAccountView lightning Component
  • In LDSAccountEdit component, add action to lightning:button with label as "Back".
<lightning:button variant="brand" label="Back" onclick="{!c.goBackToListView}"/>
  • Click CONTROLLER in the right side bar of the code editor and add goBackToListViewfunction and save the file.
goBackToListView : function(component,event,helper){
component.set("v.currView","ListView"); 
}
  • Same way in LDSAccountView, update lightning:button with label as "Back" and add "goBackToListView" JavaScript function to controller.
Let see what we have done so far!!!



So now we are loading account view and edit page using Lightning Data Services. I think this will help you to understand basic concept of Lightning data services.

Below are list of items which can be implemented to for better User experience and for learning.
  • You can add deletion of records using LDS.
  • When you click on Save in account edit page, it should refresh the list view with latest changes. In this hands on training you have to refresh the browser to see latest values of account record.
  • You can use "New Account" to create new account record using LDS.

Hope this will help in basic understanding of Lightning Data Services!!!.


Salesforce Lightning Events : Hands on Training Guide

In this blog, I am going to cover practice session to learn Salesforce Lightning Events concept. By end of this hands on training session, you will create an Lightning App which contains 2 different components and interacts between them by using Lightning events.

So lets get started.

Before starting this hands on training, set up you salesforce environment. Below are prerequisite for this session.
  • Sign up for Developer edition ( https://developer.salesforce.com/gettingstarted ) if you don’t have.
  • Enable My Domain in your org (Navigate to Set Up-My Domain).
  • After registering My Domain, deploy it for all users.
Create a Lightning Application:
  • In the Developer Console, choose File > New > Lightning Application.
  • Specify “ApplicationEventDemoApp” as the app name and click submit.
  • Specify below code in lightning app.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Application Event Demo App 
    </div> 
</aura:application>
  • Save the file.
  • Click  preview.

Upload Static Resource:
  • Download zip file from below URL:
  • Click on Setup > Static Resouce > New
  • Specify name as “LightningEventsResources” and browse zip file.
  • Specify “Public “ for Cache Control field and click on Save.

Create a IndependentHospital lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “IndependentHospital” as the component name and click submit.
  • Specify below code in lightning component.
<aura:component > 
   <div class="slds-align--absolute-center"> 
       <b>Hospital Component</b> 
   </div> 
</aura:component>
  • Save the file.
  • Add this component in “ApplicationEventDemoApp” Lightning App and click on save and preview.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Application Event Demo App 
    </div> 
    <c:IndependentHospital/> 
</aura:application>

Let see what we have done !!!



Update the IndependentHospital lightning Component with below code
  • Update the IndependentHospital component with below code to add some action to send ambulance.
<aura:component >
    <aura:attribute name="msgFromNotifier" type="String" default=""/>
    <div class="slds-card">
        <div class="slds-card__header slds-grid">
            <header class="slds-media slds-media_center slds-has-flexi-truncate">
                <div class="slds-media__figure">
                    <img src="{!$Resource.LightningEventsResources  + '/resources/hospitalImage.jpg'}" height="100" width="100"/>
                </div>
                <div class="slds-media__body">
                    <h2>
                        <a href="javascript:void(0);" class="slds-card__header-link slds-truncate" >
                            <span class="slds-text-heading_small">Hospital</span>
                        </a>
                    </h2>
                </div>
            </header>
            <div class="slds-no-flex">
                <lightning:button label="Reset" variant="brand" onclick="{!c.resetComponent}"/>
                <lightning:button label="Send Ambulance" variant="brand" onclick="{!c.sendAmbulanceMannually}"/>
            </div>
        </div>
        <div class="slds-card__body slds-card__body_inner">
            <div class="{!if(v.msgFromNotifier == '', 'slds-show','slds-hide')}">
                <div class="slds-align--absolute-center">
                    <b>Waiting for instructions</b>
                </div>
                <div class="slds-align--absolute-center">
                    <img src="{!$Resource.LightningEventsResources  + '/resources/staticAmbulance.jpg'}" height="256" width="200"/>
                </div>
            </div>
           <div class="{!if(v.msgFromNotifier == '', 'slds-hide','slds-show')}">
                <div class="slds-align--absolute-center">
                    <b> Message from Notifier : {!v.msgFromNotifier}</b>
                </div>
                <div class="slds-align--absolute-center">
                    <img src="{!$Resource.LightningEventsResources  + '/resources/animatedAmbulance.gif'}"/>
                    <audio  controls="controls" aura:id="audioclip" class="slds-hide"/>
                </div>
            </div>
        </div>
        <footer class="slds-card__footer">@sunil02kumar</footer>
    </div>
    
</aura:component>


  • Click HELPER in the right side bar of the code editor and replace code with below code and save the file.
({ 
     PlayAmbulanceSiren : function(component) { 
var rUrl = $A.get('$Resource.LightningEventsResources'); rUrl = rUrl + '/resources/ambulanceSiren.mp3'; 
console.log(rUrl); 
var domElement = component.find("audioclip").getElement(); domElement.src=rUrl; 
domElement.play(); 
      } 
})
  • Click CONTROLLER in the right side bar of the code editor and replace code with below code and save the file.
({
    sendAmbulanceMannually: function(component, event, helper) {
        component.set("v.msgFromNotifier", "sending ambulance mannually");
        helper.PlayAmbulanceSiren(component);
    },
    resetComponent :function(component, event,helper){
        component.set("v.msgFromNotifier", "");
    }
})


Create a IndependentCarAccidentNotifier lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “IndependentCarAccidentNotifier” as the component name and click submit.
  • Specify below code in lightning component and save file.
<aura:component >
    <div class="slds-card">
        <div class="slds-card__header slds-grid">
            <header class="slds-media slds-media_center slds-has-flexi-truncate">
<div class="slds-media__body">
                    <h2>
       <a href="javascript:void(0);" ><span class="slds-text-heading_small">Car Accident Notifier </span> <a>
                    </h2>
                </div>
            </header>
            <div class="slds-no-flex">
                <lightning:button label="Send Notification via App Events " variant="brand"/>
            </div>
        </div>
        <div class="slds-card__body slds-card__body_inner">
            <div class="slds-align--absolute-center">
                <img src="{!$Resource.LightningEventsResources  + '/resources/accident.jpg'}" height="256" width="200"/>
            </div>
        </div>
        <footer class="slds-card__footer">@sunil02kumar</footer>
    </div>
</aura:component>



Modify Lightning Application
  • Modify the in “ApplicationEventDemoApp” Lightning App code and click on save and preview.
<aura:applicationextends="force:slds">
<div class="slds-text-heading_largeslds-text-align_center">
Application Event Demo App
</div>
<c:IndependentHospital/>
<c:IndependentCarAccidentNotifier/>
</aura:application>


Lets see what we have done!!


Create a carAccidentAppEvent lightning Event
  • In the Developer Console, choose File > New > Lightning Event.
  • Specify “carAccidentAppEvent” as the event name and click submit.
  • Specify below code in lightning event and save file.
<aura:event type="APPLICATION" description="Event template" >
<aura:attribute name="msg" type="String" access="GLOBAL"/> 
</aura:event>


Register for event in IndependentCarAccidentNotifier Component
  • Register for event in IndependentCarAccidentNotifier Component
<aura:registerEvent name="carAccidentAppEvent" type="c:carAccidentAppEvent"/>
  • Click CONTROLLER in the right side bar of the code editor and replace code with below code and save the file.
({
    fireCarAccidentAppEvent : function(component, event, helper) {                                                   console.log('fireCarAccidentAppEvent is called');
//syntax for app event
var appEvent = $A.get("e.c:carAccidentAppEvent");
appEvent.setParams({"msg":"Car Accident Notification through Application event. Sending ambulance!!!."});
appEvent.fire();
    }
})
  • Call this javascript function on button click.
<lightning:button label="Send Notification via App Events " variant="brand" onclick="{!c.fireCarAccidentAppEvent}"/>


Handle Event in IndependentHospital Component
  • Write Event handler in hospital component. Add below code in IndependentHospital component.
<aura:handler event="c:carAccidentAppEvent" action="{!c.handleNotification}"/>
  • Create an attribute to store msg send by event.
<aura:attribute name="msgFromNotifier" type="String" default=""/>
  • Click CONTROLLER in the right side bar of the code editor and replace code with below code and save the file.
handleNotification : function(component, event, helper){ 

console.log('handleNotification is called'); 

var sentMessage= event.getParam("msg"); 

console.log('******sentMessage by app event:'+sentMessage);

component.set("v.msgFromNotifier", sentMessage);
helper.PlayAmbulanceSiren(component); 
}

Lets see what we have done!!!

Now preview the Lightning app and click on "Send notification vai App Events" button which will trigger ambulance in independentHospital component.



So after completing this, you will understand the concept of communication between 2 different components by using application events.

Hope this will help!!!





Sunday, September 17, 2017

Application Events vs Component Events : Which/How to use for handling custom events in Lightning

Lightning framework is based on event-driven architecture which allows to communicate between different events. Lightning events can be fired or handled by javascript controller. Event are triggered by user action.

As we know that along with system events, there are 2 types of custom lightning events:
  • Application Events
  • Component Events

Here I am going to explain difference between them along with code sample.

Component Events 
  1. Components events can be handled by same component or component which is present in containment hierarchy (component that instantiates or contain component).
  2. Below is syntax for creating component event.

     <aura:event type="COMPONENT" description="Event template" >                                          <aura:attribute name="msg" type="String" access="GLOBAL"/>                                               </aura:event>

  3. Below is syntax for firing component events from javascript controller.

    var accidentEvent = component.getEvent("newCarAccident"); accidentEvent.setParams({"msg":"New Message!!!."});                               accidentEvent.fire();

  4. While handling component events, we need to specify name attribute in  <aura:handler>

    <aura:handler action="{!c.handleNotification}" event="c:carAccidentComponentEvent" name="newCarAccident">

    Make sure that name attribute is same as that of name attribute while registering the event.


Application Events

  1. This kind of events can be handled by any component which is listening to it (have handler defined for event). It is kind of publish-subscribe modal.
  2. Below is syntax of creating application event.

    <aura:event type="APPLICATION" description="Event template" >                                                     <aura:attribute name="msg" type="String" access="GLOBAL"/>                           </aura:event>


  3. Below is syntax to file application events from javascript controller.

    var appEvent = $A.get("e.c:carAccidentAppEvent");               appEvent.setParams({"msg":"New Message!!!."});                                               appEvent.fire();

  4. While handling application events, no need to specify name attribute in <aura:handler>

    <aura:handler event="c:carAccidentAppEvent" action="{!c.handleNotification}"/>
System Events
  1. These events are fired automatically by the framework such as during component initialization, attribute value change, rendering etc. All Components can register for system events in their HTML markup.
  2. Few examples of system events are init, aura:waiting, aura:doneWaiting, aura:doneRendering etc.
  3. If app or component is rerendered, then init event is fired. If server side call is made in init then, aura:waiting event is fired. Once the server call is finished then aura:doneWaiting event is fired. After this aura:doneRendering is fired.
Events Best Practices:
  1. Always try to use component events. Component event usage is more localized as these can be used by same component or component in containment hierarchy.
  2. If you use application events then it may fire system events.
  3. Use Application events only if components are present in 2 different app or are not in containment hierarchy.

I have created GitHub project which contains example demo for Application events and Component Events. You can download complete code from below URL:


Hope this help!!!



More Blogs>>: 
LIGHTNING COMPONENT TO DISPLAY SETUP AUDIT TRAIL DATA    
AURA:IF vs SLDS-SHOW/HIDE    
INHERITANCE IN LIGHTNING    
FIRING EVENT FROM LIGHTNING COMPONENT AND PASSING IT TO VF PAGE    
CHANGES TO LIGHTNING DATA SERVICE IN SUMMER'17    
LIGHTNING DATA SERVICES    
PASSING LIGHTNING COMPONENT ATTRIBUTE VALUE FROM VF PAGE    
FIRE LIGHTNING EVENTS FROM VF PAGE    
DYNAMICALLY CREATING AND DESTROYING LIGHTNING COMPONENTS    
RAISING AND HANDLING CUSTOM EVENTS IN sALESFORCE lIGHTNING    
WHY TO USE DESIGN RESOURCE AND HOW TO ADD DYNAMIC OPTION TO DATASOURCE    
PASSING LIGHTNING COMPONENT ATTRIBUTE VALUE FROM VF PAGE    
PASSING INNER WRAPPER CLASS TO LIGHTNING COMPONENT    
LIGHTNING COMPONENT FOR RECORDTYPE SELECTION FOR ANY SOBJECT    
CUSTOM COMPONENT TO SHOW/HIDE SPINNER IMAGE    

Saturday, September 9, 2017

Journey from Salesforce Classic Development to Salesforce Lightning Development

Today I got chance to speak in Pune Salesforce Developer Meet up about me journey from Salesforce Classic Development (using VF pages and VF Components) to Salesforce Lightning components.

After explaining the Salesforce transition from Classic development to Salesforce Lightning framework, I covered Salesforce Lightning custom events followed by very simple Demo.

Through this blog, I am going to share the presentation which i delivered today. This presentation also contains Git hub Repository URL which contains all code which was presented in Demo.



GITHUB URL for source code:
Lightning Events Demo App

Looking forward for everyone's comments and suggestions!!!


More Blogs>>: 
LIGHTNING COMPONENT TO DISPLAY SETUP AUDIT TRAIL DATA    
AURA:IF vs SLDS-SHOW/HIDE    
INHERITANCE IN LIGHTNING    
FIRING EVENT FROM LIGHTNING COMPONENT AND PASSING IT TO VF PAGE    
CHANGES TO LIGHTNING DATA SERVICE IN SUMMER'17    
LIGHTNING DATA SERVICES    
PASSING LIGHTNING COMPONENT ATTRIBUTE VALUE FROM VF PAGE    
FIRE LIGHTNING EVENTS FROM VF PAGE    
DYNAMICALLY CREATING AND DESTROYING LIGHTNING COMPONENTS    
RAISING AND HANDLING CUSTOM EVENTS IN sALESFORCE lIGHTNING    
WHY TO USE DESIGN RESOURCE AND HOW TO ADD DYNAMIC OPTION TO DATASOURCE    
PASSING LIGHTNING COMPONENT ATTRIBUTE VALUE FROM VF PAGE    
PASSING INNER WRAPPER CLASS TO LIGHTNING COMPONENT    
LIGHTNING COMPONENT FOR RECORDTYPE SELECTION FOR ANY SOBJECT    
CUSTOM COMPONENT TO SHOW/HIDE SPINNER IMAGE