How to use Salesforce Resources in Lightning Web Component?

Salesforce ResourceSyntax to use
Static Resourceimport sampleResource from '@salesforce/resourceUrl/resourceReference';

Only, we need to enter Static Resource Name in the place of resourceReference and then refer to the property in JS file.If there is a specific namespace in your org, please put the Static Resource Name with the namespace i.e., namespace__StaticResourceName.
Custom Labelimport labelName from '@salesforce/label/labelReference';


Only, we need to enter Custom Label Name with namespace in the place of labelReference and then refer to the property in JS file. If there is no specific namespace in your org, please put the Custom Label Name with default namespace c.
Current User Idimport userId from '@salesforce/user/Id';
SVG ResourceWe can directly write down the SVG in template of the HTML file using tag.
You can also import the SVG from static resource.

11,540 total views, 3 views today

Lightning Testing Service with Jasmine

Lightning Testing Service:  Salesforce has already provided the Apex Testing framework to check the features in your Apex classes and triggers at the server side. Now, the question is how to test the Lightning Component at the client side.
Lightning Testing Service is the solution to test Lightning Component with behavior driven javascript framework Jasmine or Mocha.

In this tutorial, we will learn how to use Lightning Testing Service using the Jasmine framework. First of all, we will little bit discuss Jasmine to understand basic functionalities.

1. Jasmine has the test suite containing specific test functions using describe() function.
2. it() determines the specs means it() contains to write the actual test scenario and expectations.
3. An expectation is nothing but an assertion which will return true or false.

Step 1: Install the unmanaged package of LTS from here. Please click on LTS-Jasmine. It will open Salesforce Login UI. Please substitute the  URL to login from test, if you want to install in your developer/ production org.

Step 2:  Now, you can see the following files are uploaded to Static Resource.

lts_jasmine (zip file)- containing core Jasmine libraries.
lts_jasmineboot (javascript file)- performing all of the necessary initialization before executing the loaded environment.
lts_testutil (javascript file)- defines utility ($T) object which will use to run the spec for your lightning component.

Now, you can see one Lightning Component named lts _jasmineRunner is installed. Thanks to Salesforce to provide all these files to run Lightning Testing Sevice.

Step 3: Now we will create a javascript file called testSuite.js and upload to Static Resource named testSuite. So, in this testSuite, we will write our test specs. First, we will check Jasmine is working or not.

describe("Lightning Testing Service", function(){

	
	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
});

Step 4: Need to create a Lightning app named testSuiteApp.app where we can call the packaged Lighntning Component named lts_jasmineRunner.cmp with our testSuite file just like

<aura:application>
	<c:lts_jasmineRunner testFiles="{!$Resource.testSuite}" />
</aura:application>

Now Run the Lightning App from developer console and you will get this type of screen:

However, you can create a Lightning Component Tab with Lightning Component instead of Lightning App.

Step 5: WOW, you have done a great job. Now, just check what you have written in the testSuite file and the result and again read from Step 1 to Step 4.
So, we have learned how to run Jasmine in Salesforce. Now, we will start to learn how to write tesSuite for Lightning Component.

Step 6: Okay, so we will create a Lightning Component named “LTSPanel” and place this to Lightning App Page(by Lightning App Builder) to show the output. From now, we will continue to update the Lightning Component and testSuite file to execute the Lightning Test Service.

Step 7: To show the welcome message.

LTSPanel.cmp | to show welcome message

<aura:component implements="flexipage:availableForAllPageTypes">	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
	</lightning:card>
</aura:component>

Step 8: You will get the following output by running the Lightning Page Tab named “LTS Panel”.

Step 9: Objective: To check the static value from Lightning Component by HTML Tag.
Now, We will update the test suite file from Line no. 21 – 35.

describe("Lightning Testing Service", function(){

	
	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	describe('c:LTSPanel', function() {

         /**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });
     });
	
});

Please note this describe() is responsible to consider the respective test suite. it() contains the test suite where $T.createComponent() is calling the respective Lightning Component named “LTSPanel” to run the test case. You need to use a namespace on Lightning Component just like lightningpro:LTSPanel (where lightningpro is the namespace) if you have set the namespace for your organization.

$T.createComponent() takes three parameters such as Component Name, to set Component’s attributes, defines if the test is required when the component to be rendered [later we will discuss about the third parameter in detail].

expect() verifies the actual result with respect to the expected result. In this example, we have tried to access the value from the component by using native HTML tag such as document.getElementById(“welcomeMSGId”).textContent and compare with function toContain() which has the expected result.

Step 10: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

Note that, “Spec 1: to test Welcome Message” is printed from it() which we have written in the previous step.

Step 11: Now we will update the same Lightning Component where we will place a button named “Show All Contacts”. If the button is placed, one panel with the message will be opened at this point in time.

LTSPanel.cmp | to show the panel with the message when a button will be pressed

<aura:component implements="flexipage:availableForAllPageTypes">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
		
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!v.showOutput}">			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
				<label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
			</div>
			<aura:set attribute="else">				
				<div style="padding:0.5rem;background: rgb(22, 50, 92);">
					<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
				</div>
			</aura:set>
		</aura:if>
	</lightning:card>
</aura:component> 

LTSPanelController.js | to set the Boolean variable as True

({
    showContacts : function(component, event, helper) {
        component.set("v.showOutput", true);
     },
)}

Step 12: Now run the same tab named “LTS Panel” to show the current output. The following result is after clicking the button named “Show All Contacts”.

Step 13: Objective: To test the show/hide panel when the button is pressed through LTS.
Now, we will update the same testSuite file within the same describe(“c: LTSPanel”) function.

describe("Lightning Testing Service", function(){

	
	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("c:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });
	}); 	
});

By default the Panel is hidden that’s why this line expect(component.find(“truePanelId”)).toBeFalsy() helps to pass the test case. Now, we have clicked the button programmatically by getEvent(“press”).fire(). But, this is applicable for the only ui: button, this is not applicable for lightning: button. Now, as per Lightning component after pressed the button the respective Panel(aura: id=”truePanelId”) would be opened. So, this line expect(component.find(“truePanelId”)).toBeTruthy() returns true to pass the test case.

Step 14: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

Step 15: Now, we will test the Server Method invocation by Lightning Test Service. So, for that, we will write Apex Classes as before mentioned in the previous blog.

Selector Class: ContactSelector.apxc

public with sharing class ContactSelector {
	public static List<Contact> selectRecords(){
		return [SELECT Id,
					   Name,
					   AccountId,
					   Department,
					   lightningpro__Level__c,
					   Birthdate,
					   Email,
					   MobilePhone
					   FROM Contact LIMIT 5];
	}
}

Controller: LTSPanelController.apxc

public with sharing class LTSPanelController {
	
	@AuraEnabled
	public static List<Contact> getContacts(){
		return ContactSelector.selectRecords();
	}
}

Step 16: Now, we will update our LTSPanel component bundle.

LTSPanel.cmp | to show the Contact records

<aura:component implements="flexipage:availableForAllPageTypes" controller="LTSPanelController">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
	<aura:attribute name="contactObj" type="Object"/>
    <aura:attribute name="contactColumns" type="List"/>
    <aura:attribute name="showSpinner" type="Boolean" default="true"/>
	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!and(v.showOutput, v.showSpinner)}">				
			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
                <label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
            </div		    
			<div aura:id="spinnerId" class="slds-spinner_container">		       
				<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
					<span class="slds-assistive-text">Loading</span>		         
					<div class="slds-spinner__dot-a"></div>		         
					<div class="slds-spinner__dot-b"></div>
		       </div>
		    </div>
	 		<aura:set attribute="else">			
					<aura:if isTrue="{!v.contactObj.length>0}">						
						<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="contactsPanelId">
							<label class="slds-align_absolute-center slds-text-color_inverse">THANKS APEX, NOW I AM ABLE TO SHOW ALL CONTACTS</label>
						</div>	
						<lightning:datatable data="{!v.contactObj}" 
											 columns="{!v.contactColumns}" 
											 keyField="Id" 
											 hideCheckboxColumn="true" 
											 onrowaction="{!c.handleRowAction}" />
						<aura:set attribute="else">								
							<div style="padding:0.5rem;background: rgb(22, 50, 92);">
								<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
							</div>
						</aura:set>
					</aura:if>
			</aura:set>				
		</aura:if>
	</lightning:card>
</aura:component> 

LTSPanelController.js

({
	showContacts : function(component, event, helper) {
        component.set("v.showOutput", true);
		helper.performShowingContacts(component);
	},
})

LTSPanelHelper.js

({
	performShowingContacts : function(component) {
		var action = component.get("c.getContacts");
		this.setupPerformShowingContactsAction(component, action);
		$A.enqueueAction(action);
	},

	setupPerformShowingContactsAction : function(component, action) { 

		action.setCallback(this, function(response){
        	this.handlePerformShowingContactsResultsCallBack(component, response)
        });	
	},

	handlePerformShowingContactsResultsCallBack : function(component, response) {

		var responseState = response.getState();
		var responseError = response.getError();
		var responseValue = response.getReturnValue();

		switch(responseState){

			default: break;
			case 'NEW': break;
			case 'RUNNING': break;

			case 'SUCCESS':
				
				component.set('v.contactColumns', 
				[
					{label: 'Contact name', fieldName: 'Name', type: 'text'},
					{label: 'Department', fieldName: 'Department', type: 'text'},
					{label: 'BirthDate', fieldName: 'BirthDate', type: 'datetime'},
					{label: 'Contact Email', fieldName: 'Email', type: 'email'},
					{label: 'Contact Phone', fieldName: 'MobilePhone', type: 'phone'}				
            	]);
				component.set("v.contactObj", responseValue);
				component.set("v.showSpinner", false);
				break;

			case 'INCOMPLETE': break;
			case 'ERROR': 
				console.log('Response Error-->'+JSON.stringify(responseError));
				break;

		}
	}
})

Step 17: You will get the following output by running the Lightning Page Tab named “LTS Panel” after clicking on “Show Contacts” button.

Step 18: Now, we will see the Server method invocation to check Show Contacts by Lightning Test Service in multiple ways such as
1. Testing by Real-Time Server Method Invocation
2. Testing by the creation of data externally
3. Testing by Jasmine Spy function

Step 19: Testing by Real-Time Server Method Invocation:: This process is discouraged because invoking server actions is time-consuming and you may get the timeout error. Let’s see our testSuite file by updating from Line no. 53-69.

describe("Lightning Testing Service", function(){

	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });


        /**
        * Real Time Server Method Invocation
        **/
        it('Step 3: to verify real time server method invocation', function (done) {
        $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function (component) {
            expect(component.get("v.contactObj")).toBeNull();
            component.find("showContactsBtnId").getEvent("press").fire();            
                return $T.waitFor(function () {                    
                    //expect(component.get("v.contactObj").length).toBe(5);                    
                }) 
            }).then(function (component) {
                done();
            }).catch(function (e) {
                done.fail(e);
            });
    	});
	});
});

Step 20: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

This spec interacts with the component to call server side action and waits for the action callback to update. During this operation, you have received the error as Timed Out after waiting 3000ms. You can check the defined default time(3000ms) for the call out in “lts_testUtil” file. You can define the WINDOW.JASMINE_DEFAULT_TIMEOUT_INTERVAL in your testSuite file to resolve this issue. But, there is no guarantee. Line no. 62 is commented out. In the next step, we will check. So, we will comment this spec from Line no.56-69.

Step 21: Need to update the Lightning Component Bundle, LTSPanel.

LTSPanel.cmp | define methods to invoke server Side action for LTS

<aura:component implements="flexipage:availableForAllPageTypes" controller="LTSPanelController">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
	<aura:attribute name="contactObj" type="Object"/>
    <aura:attribute name="contactColumns" type="List"/>
    <aura:attribute name="showSpinner" type="Boolean" default="true"/>
	
	 <!-- for Lightning Test Service -->
    <aura:method name="searchContacts" action="{!c.searchContactsCallback}">
      <aura:attribute name="response" type="Object" />
    </aura:method>
    <!-- /end -->
	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!and(v.showOutput, v.showSpinner)}">				
			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
                <label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
            </div		    
			<div aura:id="spinnerId" class="slds-spinner_container">		       
				<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
					<span class="slds-assistive-text">Loading</span>		         
					<div class="slds-spinner__dot-a"></div>		         
					<div class="slds-spinner__dot-b"></div>
		       </div>
		    </div>
	 		<aura:set attribute="else">			
					<aura:if isTrue="{!v.contactObj.length>0}">						
						<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="contactsPanelId">
							<label class="slds-align_absolute-center slds-text-color_inverse">THANKS APEX, NOW I AM ABLE TO SHOW ALL CONTACTS</label>
						</div>	
						<lightning:datatable data="{!v.contactObj}" 
											 columns="{!v.contactColumns}" 
											 keyField="Id" 
											 hideCheckboxColumn="true" 
											 onrowaction="{!c.handleRowAction}" />
						<aura:set attribute="else">								
							<div style="padding:0.5rem;background: rgb(22, 50, 92);">
								<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
							</div>
						</aura:set>
					</aura:if>
			</aura:set>				
		</aura:if>
	</lightning:card>
</aura:component> 

Please check we have introduced method called “searchContacts” at Line No. 10 to invoke Server Action.

LTSPanelController.js | to call Server Action

({
	showContacts : function(component, event, helper) {
        component.set("v.showOutput", true);
		helper.performShowingContacts(component);
	},

    searchContactsCallback : function(component, event, helper) {
       helper.handlePerformShowingContactsResultsCallBack(component, event.getParam('arguments').response);        
    },
})

Step 22: Now, we will update the testSuite file. You can check from Line No. 71-101.

describe("Lightning Testing Service", function(){

	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });


        /**
        * Real Time Server Method Invocation
        **/
        /*it('Step 3: to verify real time server method invocation', function (done) {
        $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function (component) {
            expect(component.get("v.contactObj")).toBeNull();
            component.find("showContactsBtnId").getEvent("press").fire();            
                return $T.waitFor(function () {                    
                    //expect(component.get("v.contactObj").length).toBe(1);                    
                }) 
            }).then(function (component) {
                done();
            }).catch(function (e) {
                done.fail(e);
            });
    	});*/

    	/**
        * Testing by the creation of data externally
        **/
    	it('Step 4: provided the data when action callback is invoked directly', function(done) {
	        $T.createComponent("lightningpro:LTSPanel", null, true)
		        .then(function(component){
		            // Mocking out the server-side action response
		            var res = {
		                            getState : function(){
		                                return "SUCCESS";
		                            }, 
		                            getError : function(){
		                                return "ERRORS";
		                            }, 
		                            getReturnValue: function(){
		                                return [{"Name":"Contact 1"},{"Name":"Contact 2"}];
		                            }
		                      };
		            component.searchContacts(res);

		            // Assert using components interface
		            expect(component.get("v.contactObj").length).toBe(2);
		            expect(component.get("v.contactObj")[0]['Name']).toContain("Contact 1");

		            // Assert using DOM element owned by the namespace
		            //expect(component.find("contactListId").getElement().children.length).toBe(2);
		            done();
		        }).catch(function(e) {
		            	done.fail(e);
		           });
	    });

	});	
	
});

Step 23: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

You can check Spec 3 is missing because we have commented Real Time server method action from the testSuite file. You can check Line No. 78 where we have declared a variable called “res” in where we have virtually created the Contact records(Line No. 86) and defined getState and getError functions because those functions are declared in LTSPanelHelper.js(Line No. 17-18). No, we are calling “searchContacts” method of the LTSPanel component with defined “res” object. If all the actions are successful, we can compare the length of the defined “contactObj” and Name of any instance of “contactObj” by Jasmine expect() function (Line No. 92-93).

Step 24: We will test the same thing by Jasmine SPY function. For this, we need to update Lightning Component, LTSPanel with standalone method with no attributes.

LTSPanel.cmp | to declared “loadContacts” method

<aura:component implements="flexipage:availableForAllPageTypes" controller="LTSPanelController">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
	<aura:attribute name="contactObj" type="Object"/>
    <aura:attribute name="contactColumns" type="List"/>
    <aura:attribute name="showSpinner" type="Boolean" default="true"/>
	
	 <!-- for Lightning Test Service -->
    <aura:method name="searchContacts" action="{!c.searchContactsCallback}">
      <aura:attribute name="response" type="Object" />
    </aura:method>
	<aura:method name="loadContacts" action="{!c.showContacts}"/>
    <!-- /end -->
	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!and(v.showOutput, v.showSpinner)}">				
			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
                <label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
            </div		    
			<div aura:id="spinnerId" class="slds-spinner_container">		       
				<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
					<span class="slds-assistive-text">Loading</span>		         
					<div class="slds-spinner__dot-a"></div>		         
					<div class="slds-spinner__dot-b"></div>
		       </div>
		    </div>
	 		<aura:set attribute="else">			
					<aura:if isTrue="{!v.contactObj.length>0}">						
						<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="contactsPanelId">
							<label class="slds-align_absolute-center slds-text-color_inverse">THANKS APEX, NOW I AM ABLE TO SHOW ALL CONTACTS</label>
						</div>	
						<lightning:datatable data="{!v.contactObj}" 
											 columns="{!v.contactColumns}" 
											 keyField="Id" 
											 hideCheckboxColumn="true" 
											 onrowaction="{!c.handleRowAction}" />
						<aura:set attribute="else">								
							<div style="padding:0.5rem;background: rgb(22, 50, 92);">
								<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
							</div>
						</aura:set>
					</aura:if>
			</aura:set>				
		</aura:if>
	</lightning:card>
</aura:component> 

Step 25: Now, we will update testSuite file with Jasmine SPY function from Line No. 104-136.

describe("Lightning Testing Service", function(){

	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });


        /**
        * Real Time Server Method Invocation
        **/
        /*it('Step 3: to verify real time server method invocation', function (done) {
        $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function (component) {
            expect(component.get("v.contactObj")).toBeNull();
            component.find("showContactsBtnId").getEvent("press").fire();            
                return $T.waitFor(function () {                    
                    //expect(component.get("v.contactObj").length).toBe(1);                    
                }) 
            }).then(function (component) {
                done();
            }).catch(function (e) {
                done.fail(e);
            });
    	});*/

    	/**
        * Testing by the creation of data externally
        **/
    	it('Step 4: provided the data when action callback is invoked directly', function(done) {
	        $T.createComponent("lightningpro:LTSPanel", null, true)
		        .then(function(component){
		            // Mocking out the server-side action response
		            var res = {
		                            getState : function(){
		                                return "SUCCESS";
		                            }, 
		                            getError : function(){
		                                return "ERRORS";
		                            }, 
		                            getReturnValue: function(){
		                                return [{"Name":"Contact 1"},{"Name":"Contact 2"}];
		                            }
		                      };
		            component.searchContacts(res);

		            // Assert using components interface
		            expect(component.get("v.contactObj").length).toBe(2);
		            expect(component.get("v.contactObj")[0]['Name']).toContain("Contact 1");

		            // Assert using DOM element owned by the namespace
		            //expect(component.find("contactListId").getElement().children.length).toBe(2);
		            done();
		        }).catch(function(e) {
		            	done.fail(e);
		           });
	    });


	    /**
        * Server side action Testing by Jasmine SPY function
        **/
	    it('Step 5: SPY - FUNCTION:: updates with provided data when action callback is invoked directly- spy ', function(done) {
	        $T.createComponent("lightningpro:LTSPanel", null, true)
	        .then(function(component){
	            // Mocking out the server-side action response
	            var res = {
	                            getState : function(){
	                                return "SUCCESS";
	                            }, 
	                            getError : function(){
	                                return "ERRORS";
	                            },
	                            getReturnValue: function(){
	                                return [{"Name":"Contact 1"},{"Name":"Contact 2"}];
	                            }
	                      };

	            spyOn($A, "enqueueAction").and.callFake(function(action) {
	                    var cb = action.getCallback("SUCCESS")
	                    cb.fn.apply(cb.s, [res]);
	                });
	            component.loadContacts();

	            // Assert using components interface
	            expect(component.get("v.contactObj").length).toBe(2);
	            expect(component.get("v.contactObj")[0]['Name']).toContain("Contact 1");
	            done();
	        }).catch(function(e) {
	            done.fail(e);
	        });
    	});

	});	
	
});

Step 26: Please run the testSuite.app file from developer console to see the output.

Please check from the Line no. 123 – 127 of your testSuite file. We have defined Jasmine Spy function at Line no. 123 to satisfy Jasmine’s mocking capabilities. In this process, the biggest advantage is that you don’t need to break the functions into the component’s helper.js file. Only you need to declare the method in the Lightning Component. This approach is suggested to check the server side action by Lightning Test Service.

Step 27: Now, question is that how will we cover event handling testing by Lightning Test Service. In this tutorial, we will not cover event handling in detail. If you have used component event and application event, please update your testsuite file with the following spec.

it('Spec 6: to check Component and Application Event', function(done) {
	$T.createComponent("lightningpro:LTSPanel", null)
	.then(function(component){
		var componentEvent = component.getEvent("cmpEvent");
		componentEvent.setParams({"data":"component event fired"});
		componentEvent.fire()
		expect(component.get("v.message")).toBe("component event fired");
		$T.fireApplicationEvent("lightningpro:appEvent", {"data":"application event fired"});
		expect(component.get("v.message")).toBe("application event fired");
		done();
		}).catch(function(e) {
			done.fail(e);
		});
});

Remember, in this example “lightningpro” is the namespace of my Org. If your org has not any namespache please use “c” instaed of using “lightningpro”.

A big thank you for spending your valuable time to understand the Lightning Testing Service with Jasmine.
                                                                   Again, Thank You.

5,397 total views, 4 views today

Method vs Component Event in Lightning

Already, we have studied about Method and Component Event in Lightning.Now, we will focus on the difference between Method and Component Event in Lightning.

 MethodComponent Event
WHENWhen the communication is needed from Parent Component to Child Component,we can use .

Method can be synchronous or asynchronous.
When the communication is needed from Child Component to Parent Component,we can use component event.

If you want to build reverse communication process, you can think about Component Event with Capture phase.

Component Event can be synchronous, but Application Event can be asynchronous.
WHYAt the time of component rendering, best practice to call the method with parameters in that component.

At the time of calling Child Component, you can call the method of Child Component explicitly to render/view the Child Component with certain attributes in proper.

At the time of extend the component, you can use the method and set the attribute directly from the inherited component.

Component Event basically occurs from bottom to top in the same containment hierarchy. In this model, at first you have to register the Component Event, fire the event and receiving the event from other immediate component present in the same containment hierarchy.
ADVANTAGEFast
Require less network bandwidth
communication speed depends on the level of containment hierarchy.

3,564 total views, 6 views today

How to use method in Lightning Component?

You can call a method using aura:method tag in a component’s client side controller instead of using Component Event.
In short,
aura:method communicates down the Containment Hierarchy that means a Parent Component can call aura:method on a child component in the same Containment Hierarchy. Communication Path: [Parent Component -> Child Component]

Component Event communicates up the Containment Hierarchy that means Child Component sends the message to Parent Component by Component Event. Communication Path: [Child Component -> Parent Component]

The communication for method is of two types:

1.Synchronous – Generally, a Synchronous method finishes executing before it returns. In this communication, this method will not be responsible to call Apex Class.

2. Asynchronous – Generally, Asynchronous code can continue to execute after return operation. In this communication, method is responsible to call Apex Class from javascript controller.

Now we will discuss about aura:method in detail.

Method Syntax

<aura:method name="invokeMethod" action="{!c.callAction}">
	<aura:attribute name="input1" type="String"/>
	<aura:attribute name="input2" type="String"/>
</aura:method>

Synchronous Communication

Defining Process

1. We will create Child Component bundle named “childMethodComponent” where aura:method will be defined.

childMethodComponent.cmp | defines the aura:method

<aura:component> 
    <aura:attribute name="message" type="String" description="This is used to hold the values sent by Parent Component"/>
    <aura:method name="invokeMethod" action="{!c.callAction}">
         <aura:attribute name="input1" type="String"/>
         <aura:attribute name="input2" type="String"/>
    </aura:method> 
    {!v.message}
</aura:component>

childMethodComponentController.js | defines the function(“callAction”) when method will be invoked

({
	callAction : function(component, event, helper) {
		var params = event.getParam('arguments');
		if (params) {
			var param1 = params.input1;
            var param2 = params.input2;
		}
        
        console.log("param1--->"+param1);
        console.log("param2--->"+param2);
        component.set("v.message",param1+'---'+param2);
    }
})

Calling Process

2. We will create Parent Component bundle named “parentMethodComponent” from where we will call the method of the child component.

parentMethodComponent.cmp | calling the child component named”childMethodComponent.cmp

<aura:component>
    <c:childMethodComponent aura:id="childComponentId"/>
    <lightning:button label="Call Method" onclick="{!c.callMethod}"/>
</aura:component>

parentMethodComponentController.js | calling the method(“invokeMethod“) of child component

({
	callMethod : function(component, event, helper) {
		var childComponentId = component.find("childComponentId");
        childComponentId.invokeMethod('a','b');
	}
})

So, we have learnt Method for Synchronous Communication. Now, we will focus on Method for Asynchronous Communication.

Asynchronous Communication

In this process, we will show the latest 5 Contact records in the Parent Component by calling the method of the Child Component.

1. In Asynchronous communication, we will get response from Server Side. So, we will create Apex Class named “ShowContactsController” to return latest 5 Contact records.

ShowContactsController.apxc | to return latest 5 Contact records by invoking method in javascript controller

/* @name: ShowContactsController 
   @description: return the latest 5 Contact Records*/

public class ShowContactsController {
    
    @AuraEnabled
	public static List<Contact> getContactRecords(){
        
        List<Contact> contactList = [SELECT Id, Name FROM Contact ORDER BY LastModifiedDate Desc LIMIT 5 ];
        if(contactList <> NULL && contactList.size()>0)
        	return contactList;
        else
            return NULL;
    }
}

2.Now, same components we will update for learning purpose.

childMethodComponent.cmp | defines the aura:method

<aura:component controller="ShowContactsController">	
    <aura:method name="invokeMethod" action="{!c.callAction}">
        <aura:attribute name="callback" type="Function"/>
    </aura:method>   
</aura:component>

Please notice that at Line No. 3, we have declared an attribute of type Function to callback.

childMethodComponentController.js | calling the helper.js with callback attribute

({
	callAction : function(component, event, helper) {
        var params = event.getParam('arguments');
        var callback;
        if (params)
            callback = params.callback;
        
		helper.performShowingContacts(component, callback);
    }
})

childMethodComponentHelper.js | to call the method named “getContactRecords” of the Apex Class “ShowContactsController”

({
      
    performShowingContacts : function(component, callback) {
        var action = component.get("c.getContactRecords");
        this.setupPerformShowingContactsAction(component, action, callback);
        $A.enqueueAction(action);
    },
     
    setupPerformShowingContactsAction : function(component, action, callback) { 
 
        action.setCallback(this, function(response){
            this.handlePerformShowingContactsResultsCallBack(component, response, callback);
        }); 
    },
     
    handlePerformShowingContactsResultsCallBack : function(component, response, callback) {
 
        var responseState = response.getState();
        var responseError = response.getError();
        var responseValue = response.getReturnValue();
 
        switch(responseState){
 
            default: break;
            case 'NEW': break;
            case 'RUNNING': break;
 
            case 'SUCCESS':
                 
                if(!$A.util.isEmpty(responseValue)){
                     if (callback) 
                         callback(response.getReturnValue());
                }
                 
                break;
                 
            case 'INCOMPLETE': break;
            case 'ERROR': 
                console.log('Response Error-->'+JSON.stringify(responseError));
                break;
        }
         
    }
   
})

Please notice that at Line No. 31-32, this Child Component returning the result from Server Side to Parent Component by calling “callback” function.

3. Need to update ParentMethodComponent.

parentMethodComponent.cmp | to show the latest 5 contact records

<aura:component>
    <aura:attribute name="contactList" type="List" description="This is used to store the Contact Records returned from Server Side"/>
    <c:ChildMethodComponent aura:id="childComponentId"/>    
 	
    <lightning:card>  
        <aura:set attribute="title">
            Contacts
        </aura:set>
        <aura:set attribute="actions">
            <lightning:button label="Show Contacts" onclick="{!c.callMethod}"/>
        </aura:set>
        <aura:if isTrue="{!v.contactList.length>0}">
            
        
        <div class="slds-m-left_medium">
            <aura:iteration items="{!v.contactList}" var="con">
                {!con.Name}
            </aura:iteration> 
        </div>


        </aura:if>
    </lightning:card>
</aura:component>

parentMethodComponentController.js | to invoke the Child Component’s method and set the List variable to the result returning from Child Component

({
	callMethod : function(component, event, helper) {
		var childComponentId = component.find("childComponentId");
        childComponentId.invokeMethod(function(result) {
            console.log("result: " + JSON.stringify(result));
            component.set("v.contactList", result);
        });
	}
})

Please notice that at Line No. 4-7, calling the method named “invokeMethod” of a child component and set the contactList to the result of contact records coming from Child Component via Method using Apex Class.

4. Result

Run the Lightning Component(parentMethodComponent.cmp) from Lightning Application for Learning Purpose. Otherwise, you can use of Lightning App Builder.

5,668 total views, 1 views today

Lightning Flow Overview

According to Salesforce, Lightning Flow consists of Process Builder and Cloud Flow Designer.

Process Builder is responsible to create process when record is created/updated or any platform event is occurred.
Cloud Flow Designer is used to create flows which is used to automate business process by taking input from User or by collecting data in Salesforce Org or from an external system.

Type of Flows

Screen Flow – Requires User Interaction
Autolaunched Flow – handle back-end services, so It is not supported screens, choices, dynamic choices, local actions and steps.
User Provisioning Flow – It is implemented with a connected app when running the User Provisioning Wizard.
Field Service Mobile Flow – Requires User Interaction (used in Field Service Lightning mobile app)
Field Service Snap-In Flow – Requires User Interaction (used in Snap-ins Appointment Booking)

Flow Distribution

We can use Screen Flow from

Web tab
Utility bar
Flow actions
Custom buttons and links
Visualforce Page
Lightning Page
Custom Lightning component
Lightning Community page
Direct flow URLs
Lightning Out

We can use Autolaunched Flow from

Process Builder
Custom Apex classes
REST API
Web tabs
Custom buttons or custom links
Visualforce pages

Order of execution

After Workflow field updates, Lightning Flow is executed. Please visit Salesforce Order of execution link.

Flow Runtime Experience

Please enable Lightning runtime for flows from Process automation Settings(from Setup in your org) to make the UI feel like Lightning Experience for only URL-based flows means applicable for the flows which are generated from Custom Button, Custom Link, Web Tab and Direct Link. This settings is not required for Lightning Component, Lightning Pages & Lightning Community Pages. This setting is applicable for only URL-based flows.

 

3,197 total views, 3 views today

Create Record By Lightning Data Service

Create Record By Lightning Data Service

In this section, we will create a Contact Record from Account using Lightning Data Service(LDS) in Lightning Experience.

Step 1: Create a component bundle named “createContactByLDS”.

createContactByLDS.cmp | to create Contact record from Account by LDS

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

    <aura:attribute name="account" type="Object"/>
    <aura:attribute name="loadedAccountFields" type="Object"/>
    <aura:attribute name="accountError" type="String"/>

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

    <force:recordData aura:id="accountRecordLoaderId"
        recordId="{!v.recordId}"
        fields="Name"
        targetRecord="{!v.account}"
        targetFields="{!v.loadedAccountFields}"
        targetError="{!v.accountError}"
    />

    <force:recordData aura:id="contactRecordCreatorId"
        layoutType="FULL"
        targetRecord="{!v.newContact}"
        targetFields="{!v.newContactFields}"
        targetError="{!v.newContactError}" 
    /> 

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

    <div class="slds-page-header" role="banner">
        <p class="slds-text-heading_label">{!v.loadedAccountFields.Name}</p>
        <h1 class="slds-page-header__title slds-m-right_small
            slds-truncate slds-align-left">Create New Contact</h1>
    </div>
    <lightning:card >
        <aura:if isTrue="{!not(empty(v.accountError))}">
            <div class="recordError">
                <ui:message title="Error" severity="error" closable="true">
                    {!v.accountError}
                </ui:message>
            </div>
        </aura:if>
        <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>

        <lightning:layout multiplerows="true" verticalalign="center">
            <lightning:layoutItem padding="around-small" size="12">
                <lightning:input aura:id="contactField" 
                                 name="firstName" 
                                 label="First Name"
                                 value="{!v.newContactFields.FirstName}"/>
              
                <lightning:input aura:id="contactField" 
                                 name="lastname" 
                                 label="Last Name"
                                 value="{!v.newContactFields.LastName}" 
                                 required="true"/>
                        
                <!--<lightning:input aura:id="contactField" 
                                 type="phone" 
                                 name="phone" 
                                 label="Phone Number"
                                 pattern="^(1?(-?\d{3})-?)?(\d{3})(-?\d{4})$"
                                 messageWhenPatternMismatch="The phone number must contain 7, 10, or 11 digits. Hyphens are optional."
                                 value="{!v.newContactFields.Phone}" 
                                 required="true"/>-->
                
                <lightning:input aura:id="contactField" 
                                 type="email" 
                                 name="email" 
                                 label="Email"
                                 value="{!v.newContactFields.Email}" />
                
                <div class="slds-float_right">
                    <lightning:button label="Reset" onclick="{!c.doReset}" class="slds-m-top_medium" />
                    <lightning:button label="Save" onclick="{!c.saveContact}"
                                      variant="brand" class="slds-m-top_medium"/>
                </div>                
           </lightning:layoutItem>
       </lightning:layout>    
    </lightning:card>
</aura:component>

createContactByLDSController.js | to call the standard Save operation by LDS

({
    doInit: function(component, event, helper) {
        helper.openNewContact(component);
    },

    saveContact: function(component, event, helper) {
        if(helper.validateContactForm(component)) {
            component.set("v.newContactFields.AccountId", component.get("v.recordId"));
            component.find("contactRecordCreatorId").saveRecord(function(saveResult) {
                if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {

                    var resultsToast = $A.get("e.force:showToast");
                    resultsToast.setParams({
                        "title": "Success! ",
                        "message": "The new contact was created.",
                        "type":"success"
                    });

                    $A.get("e.force:closeQuickAction").fire();
                    resultsToast.fire();

                    $A.get("e.force:refreshView").fire();
                    helper.openNewContact(component);
                }
                else if (saveResult.state === "INCOMPLETE") {
                    console.log("User is offline, device doesn't support drafts.");
                }
                else if (saveResult.state === "ERROR") {
                    console.log('Problem saving contact, error: ' +
                                 JSON.stringify(saveResult.error));
                }
                else {
                    console.log('Unknown problem, state: ' + saveResult.state +
                                ', error: ' + JSON.stringify(saveResult.error));
                }
            });
        }
    },

    doReset: function(component, event, helper) {
        //$A.get("e.force:closeQuickAction").fire();
        helper.openNewContact(component);
    },
})

createContactByLDSHelper.js | to reset and validate the form of the Contact

({
    
    openNewContact: function(component){
        component.find("contactRecordCreatorId").getNewRecord(
            "Contact", // 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);
                }
                else {
                    console.log("Record template initialized: " + rec.sobjectType);
                }
            })
        );
    },


    validateContactForm: function(component) {
        var validContact = true;
        var allValid = component.find('contactField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);

        if (allValid) {
            var account = component.get("v.account");
            if($A.util.isEmpty(account)) {
                validContact = false;
                console.log("Quick action context doesn't have a valid account.");
            }
        	return(validContact);
            
        }  
	}
       
})

We have used openNewContact form to instantiate the Contact record means every time it will open the blank Contact template at the time of loading contact or at the time of doReset function is called from controller.js file.

We are validating the contact by using standard javascript reduce function. The standard showHelpMessageIfInvalid() throws the error message for the lightning:input where value is missing or pattern is not matched.

You can get the details in the reference link: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/data_service_example.htm#data_service_example

Step 2: That’s it. Now just add this component into the Lightning record Page of an Account.

Results

Notes

1.

<force:recordData aura:id="accountRecordLoaderId"
        recordId="{!v.recordId}"
        fields="Name"
        targetRecord="{!v.account}"
        targetFields="{!v.loadedAccountFields}"
        targetError="{!v.accountError}"
    />

This is responsible to collect account name with respect to record Id.

2.

<force:recordData aura:id="contactRecordCreatorId"
        layoutType="FULL"
        targetRecord="{!v.newContact}"
        targetFields="{!v.newContactFields}"
        targetError="{!v.newContactError}" 
    /> 

This is responsible to create contact record. Please notice that, we have used layoutType instead of using fields. So, at the time of definition of Contact fields in the form, we can use any field resides on the Contact Object.

3. In createContactByLDSController.js file, we have set the Account Id of Contact just like component.set(“v.newContactFields.AccountId”, component.get(“v.recordId”)).

4. In createContactByLDSHelper.js file, we need to declare the instantiation of the Contact Record in the “openNewContact” function at Client Side for Lightning Data Service.

In the same file, we have used “validateContactForm” function to validate the Contact form.

5,885 total views, 3 views today

Load Record by Lightning Data Service

Loading record by Lightning Data Service

In Lightning Experience, we will load the Account record with some attributes from Contact by Lightning Data Service.

Step 1: Need to create a Lightning Component named “loadAccount“.

loadAccount.cmp | It will show the respective Account Details from the Contact record page.

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

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

    <!-- Lightning Data Service -->
    <force:recordData aura:id="accountLoaderId"
				      recordId="{!v.recordId}"
				      fields="Name, Account.Name, Account.BillingCity, Account.BillingState"
				      targetFields="{!v.loadedRecordFields}"
				      targetError="{!v.recordError}"/>    
    
    <!-- Account Details Section -->
    <lightning:card iconName="standard:contact" title="{!v.loadedRecordFields.Name}" >
    	<div class="slds-p-horizontal--small">
        	<p class="slds-text-heading--small">
        		<div class="demo-only" style="padding: 0.5rem; background: rgb(22, 50, 92);">
  					<div class="slds-text-color_inverse">Account Details</div>
				</div><br/>
                <b>Name:</b> <lightning:formattedText title="Account Name" value="{!v.loadedRecordFields.Account.Name}" />
            </p>
            <p class="slds-text-heading--small">
                <b>BillingCity:</b> <lightning:formattedText title="Billing City" value="{!v.loadedRecordFields.Account.BillingCity}" />
            </p>
            <p class="slds-text-heading--small">
                <b>BillingState:</b> <lightning:formattedText title="Billing State" value="{!v.loadedRecordFields.Account.BillingState}" />
            </p>
        </div>
    </lightning:card>
 
 	<!-- Errors, if any -->
	<aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            {!v.recordError}</div>
    </aura:if>
</aura:component>

Step 2: Please open any Contact record in Lightning Experience & click on “Edit Page” by Settings(Gear Icon at the top-right corner) and drag the “loadAccount” component to the right side of the Contact Lightning Record Page. After this, please save and activate the contact page.

Result
loaded account

You can see the red color highlighted portion of the Contact page and it will show the Account details from Contact: Mr. John Bond.

Notes

If you will use layoutType=”FULL” into “force:recordData“, you can only refer to the Name field of Account(Account.Name) from Contact, you can not refer other Account fields from Contact. That’s why, we have mentioned all the respective Account fields (Account.Name, Account.BillingCity, Account.BillingState) and Contact field(Name) both to query in the “fields” attribute. So, we have used “fields” instead of using “layoutType”.

3,365 total views, 4 views today

Lightning Data Service

What is Lightning Data Service(LDS)?
-Lightning Data Service is the data layer for Lightning. We can load, create, edit or delete a particular record by LDS without calling Server/Apex Class. You can compare Standard Visualforce Controller to Lightning Data Service.

PROS

1. LDS supports sharing rules and field level security.
2. Record loaded by LDS is cached and shared across all components.
3. Minimize XMLHttpRequests.
4. After record changes, notification process is available. You can compare with publish-subscriber model.
5. It is offline supported.

CONS

1. LDS is applicable to operate on a particular or single record at once. LDS is not supported for multiple records at once.
2. LDS is only applicable in Lightning Experience. It is not supported in Lightning Components calling from Visualforce page or Lightning Out. If this Visualforce Page is added into Lightning Experience or in Salesforce Mobile app, then LDS is supported.

Syntax


<force:recordData aura:id="accountInfoId" 
                  recordId="{!v.recordId}" 
                  mode="EDIT" layoutType="FULL" 
                  fields="Id, Name, Phone" 
                  targetRecord="{!v.record}" 
                  targetFields="{!v.simpleRecord}" 
                  targetError="{!v.errors}" 
                  recordUpdated="{!c.updateRecord}" />

A. force:recordData tag is to define Lightning Data Service.
B. force:recordData has some parameters to fulfill the Lightning Data Service. We will discuss one by one.

1. recordId: It defines the Record Id for which LDS will run.
2. mode: It can be “EDIT” or “VIEW“. It will tell the mode of operation.
3. layoutType: It will specify the Layout such as “FULL” or “COMPACT“.
4. fields: LDS will query for these fields internally.
5. fields or layoutType is required to query data by LDS.
6. targetRecord: It is populated with the loaded record.
7. targetFields: It is populated with the simplified view of the loaded record.
8. targetError: It is responsible to show the errors, if errors are occurred during operation.
9. recordUpdated: It is responsible to do certain operation if anything changes to the loaded record. Remember, for only this operation, we are calling method in javascript”{!c.updateRecord}”.

In the next articles, we will see how to load, create, edit or delete record using Lightning Data Service.

4,451 total views, 6 views today

Convert a List from a Map in Lightning Component

Convert a List from a Map in Lightning Component

In many projects, we have seen that we need to iterate a Map in Lightning component to satisfy the business requirement. But, it’s not possible in straightforward to iterate the Map at Component level. We have a solution for this. We can create a List from Map. We can create a List with Key and Value identifiers from a Map and then iterate the List and compare the Key and Value, if applicable.

Step 1: Need to create a Selector Class to return all the Contact records in a Map.

ContactSelector.apxc |  to return the Map of Contact records

public with sharing class ContactSelector {
	public static Map<Id,Contact> selectRecordsMap(){		
		return new Map<Id, Contact>([SELECT Id, Name, AccountId FROM Contact]);
	}
}

Step 2: To call the Selector Class(ContactSelector) to get the Map of Contact records.

SimpleMaptoListController.apxc |  to call the Selector Class “ContactSelector

public with sharing class SimpleMaptoListController {
	
	@AuraEnabled
	public static Map<Id,Contact> getContacts(){
		return ContactSelector.selectRecordsMap();
	}
}

Step 3: Create a Component Bundle named “simpleMaptoList”.

simpleMaptoList.cmp | to iterate the List converted from a Map returned from Server Side

<aura:component implements="flexipage:availableForAllPageTypes" controller="SimpleMaptoListController">
	<aura:attribute name="selectedContact" type="String"/>
	<aura:attribute name="contactList" type="List"/>
	
	<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

	<lightning:card title="">
		<aura:set attribute="title">
			<lightning:icon iconName="utility:connected_apps" size="small"/>
			<span style="font-size: 2rem;" >Map to List</span>
		</aura:set>
		<lightning:layout multiplerows="true" verticalalign="center">
			<lightning:layoutItem padding="around-small" size="6">				
				<lightning:select name="Select a Contact" label="Select a Contact" aura:id="contactSelect" value="{!v.selectedContact}">
			        <aura:iteration items="{!v.contactList}" var="con">
                                    <option text="{!con.value}" value="{!con.key}"/>
			         </aura:iteration>
				</lightning:select> 
			</lightning:layoutItem>
		</lightning:layout>
	</lightning:card>
</aura:component>

simpleMaptoListController.js |  to call the helper function

({
	doInit : function(component, event, helper) {
		helper.performShowingContacts(component, event);
 	}
})

simpleMaptoListHelper.js | to convert a List from a Map returned from Server Side and to set the List variable in the Component

({
	performShowingContacts : function(component, event) {
		var action = component.get("c.getContacts");
		//action.setStorable();
		this.setupPerformShowingContactsAction(component, action);
		$A.enqueueAction(action);
	},

	setupPerformShowingContactsAction : function(component, action) {

		action.setCallback(this, function(response){
        	this.handlePerformShowingContactsResultsCallBack(component, response);
        });	
	},

	handlePerformShowingContactsResultsCallBack : function(component, response) {

		var responseState = response.getState();
		var responseError = response.getError();
		var responseValue = response.getReturnValue();

		var contacts = [];
		
		switch(responseState){

			default: break;
			case 'NEW': break;
			case 'RUNNING': break;

			case 'SUCCESS':

				if(responseValue != null){
					for(var key in responseValue){
						contacts.push({
							key: key,
							value: responseValue[key].Name
						});
					}
				}
				component.set("v.contactList", contacts);
				break;
					

			case 'INCOMPLETE': break;
			case 'ERROR': 
				console.log('Response Error-->'+JSON.stringify(responseError));
				break;

		}
	}
})

Steps to Remember

1. Create Apex Class to return the Map to Lightning Component.
2. Declare the List attribute in .cmp file.
3. In helper.js file, declare an array (in this assignment, we have declared an array named “contacts[]”) in which we will store the Map with Key-Value Pair.
4. Then, need to push the Key – Value pair into this array by looping the responseValue(Map) from Server Side just like

for(var key in responseValue){
 contacts.push({
 key: key,
 value: responseValue[key].Name
 });
}

5. Need to set the List attribute of component with this array which we have created in helper.js file.
6. At last, you have to iterate the List attribute to show the result in your component just like

<aura:iteration items="{!v.contactList}" var="con">
  <option text="{!con.value}" value="{!con.key}"/>
</aura:iteration>

8,672 total views, 5 views today

How to use Map in Lightning Component?

Map: This is an collection of Key-Value pair items. A Map can’t contain duplicate keys.

How to use Map in Lightning Component?

A. How can we define and access the Map in Lightning Component?
B. How can we define and access the Map in Lightning Component through Server?

A. How can we define and access the Map in Lightning Component?

Step 1: Create a Lightning Component Bundle named “simpleMap”.

simpleMap.cmp | showing the values from a map

<aura:component implements="flexipage:availableForAllPageTypes">

	<aura:attribute name="capitalMap" type="Map" default="{key1:'value1', key2:'value2'}"/>
	
	<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
			
	<span style="font-size: 1rem;">Capital</span>

	The capital of India: {!v.capitalMap.India}
	The capital of Nepal: {!v.capitalMap.Nepal}
	
					
</aura:component>

simpleMapController.js | define the map at controller.js file

({
	doInit : function(component, event, helper) {
		var capitalMap = component.get("v.capitalMap");

		//capitalMap["KEY"] = "VALUE";
		capitalMap["India"] = "New Delhi";
		capitalMap["Nepal"] = "KathMandu";
		
		component.set("v.capitalMap",capitalMap);
        console.log("The Capital of India: "+component.get("v.capitalMap")["India"]);		
	}
})

B. How can we define and access the Map in Lightning Component through Server?

Step 1: Need to write Apex Class where we will define the Map at Class level.

SimpleMapController.apxc | returns the map from server side

public with sharing class SimpleMapController {
	
	@AuraEnabled
	public static Map<String, String> getCapitals(){

		Map<String, String> capitalsMap = new Map<String, String>();
		capitalsMap.put('India', 'New Delhi');
		capitalsMap.put('Nepal', 'KathMandu');

		return capitalsMap;
		
	}
}

Step 2: Create a Lightning Component Bundle named “simpleMap”.

simpleMap.cmp | showing the values from a map

<aura:component implements="flexipage:availableForAllPageTypes" controller="SimpleMapController">

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

	<span style="font-size: 1rem;">Capital</span>

	The capital of India: {!v.capitalsMap.India}
	The capital of Nepal: {!v.capitalsMap.Nepal}
					
</aura:component>

simpleMapController.js | to call the helper function

({
	doInit : function(component, event, helper) {
		helper.performShowingCapitals(component, event);
 	}
})

simpleMapHelper.js | to set the map attribute of the component to the map returned from server side

({
	performShowingCapitals : function(component, event) {
		var action = component.get("c.getCapitals");
		this.setupPerformShowingCapitalsAction(component, action);
		$A.enqueueAction(action);
	},

	setupPerformShowingCapitalsAction : function(component, action) {

		action.setCallback(this, function(response){
        	this.handlePerformShowingCapitalsResultsCallBack(component, response);
        });	
	},

	handlePerformShowingCapitalsResultsCallBack : function(component, response) {

		var responseState = response.getState();
		var responseError = response.getError();
		var responseValue = response.getReturnValue();

		switch(responseState){

			default: break;
			case 'NEW': break;
			case 'RUNNING': break;

			case 'SUCCESS':

				if(responseValue != null)			
					component.set("v.capitalsMap", responseValue);
				break;
					

			case 'INCOMPLETE': break;
			case 'ERROR': 
				console.log('Response Error-->'+JSON.stringify(responseError));
				break;

		}
	}
})

Steps to remember

1. Declare the attribute of Map type in Component Level.
2. Need to set the Map sent by Apex Class or standalone in your javascript file to Map attribute in your Lightning Component by javascript file.
3. Need to print out the value of the Map in Component file in this way: {!v.mapAttribute.key}
4. Need to print out the value of the Map in javascript file in this way: component.get(“v.mapAttribute”)[“key”]

6,057 total views, 1 views today