Create First Lightning Web Component

Every Lightning Web Component (LWC) has the following files such as

  1. html [defines the UI layer with html content]
  2. js [defines the behavior of the LWC]
  3. css [defines the look & feel]
  4. svg [defines the Scalable Vector Graphics for the Lightning Web Component]
  5. xml [defines the metadata of the LWC to set the component visibility in the Lightning Experience along with configuration of design attributes for Lightning App Builder]

Let’s say, we are going to create one Lightning Web Component named “welcomeWindow“. So, following files will be created by default in the root folder of “welcomeWindow” Lightning Web Component.

welcomeWindow.html
welcomeWindow.js
welcomeWindow.js-meta.xml

You can create the welcomeWindow.css and welComeWindow.svg file, but it’s optional.

1. Let’s talk about welcomeWindow.js-meta.xml first.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="welcomeWindow">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

At the time of saving this XML file, please set the value for isExposed tag as true. That means, this component will be visible into Lightning App Builder. If you focus the targets, you can see that we have passed the values for the target as lightning__AppPage, lightning__RecordPage, lightning__HomePage.

lightning_AppPage: LWC is visible for Lightning App.
lightning_RecordPage: LWC is visible for Lightning Record Page.
lightning_HomePage: LWC is visible for Lightning Home Page.

Later we will discuss about other separate configuration tags belongs to XML file.

2. Then, we need to focus on welcomeWindow.html file.

<!-- @name: welcomeWindow @description: used to show only welcome message by Lightning Web Component @author: Santanu Pal @date: 10 FEB 2019 Copyright (c) 2019 -->

<template>
    <lightning-card title="Welcome My Friends">    
        <lightning-layout>
                <lightning-layout-item flexibility="auto" padding="around-small">                    
                        Welcome to LWC Training Class
                </lightning-layout-item>
            </lightning-layout>
    </lightning-card>     
</template>

3. For now, we will not touch the welcomeWindow.js & welcomeWindow.css file.

4. You can see this Lightning Web Component (LWC) in any Lightning App Page, Lightning Record Page & Lightning Home Page based on welcomeWindow.js-meta.xml file.

Result

So, we have created our first Lightning Web Component which is currently displaying only welcome message.

Now, if we will inspect our first Lightning Web Component, we will see the name of our Lightning Web Component is replaced with this <c-welcome-window> in the default namespace. Please see this image:

 

 

 

 

Now, why welcome-window??  Let’s go to the next chapter.

 

Thanks for your time.

8,941 total views, 9 views today

Shadow DOM in Web Component

Shadow DOM is a mechanism to encapsulate the DOM Tree. Shadow DOM is not a part of the main DOM tree. It prevents overriding the style or behavioral of the respective markup elements.

Following image describes the Shadow DOM from the main DOM tree.

Let’s assume, Document is the main root node of the main DOM tree.

Shadow Host is the link node between Shadow Tree and the main DOM tree. Shadow Tree is used to hidden DOM trees from the regular main DOM tree. Shadow Tree starts from Shadow Root(root node of the Shadow DOM tree) which is attached to Shadow Host.

Shadow Boundary is the boundary line where regular DOM tree begins and Shadow DOM Tree ends.

 

 

 

 

 

 

 

 

 

 

 

We can take the same example as described in previous session. Let’s first focus on the HTML file.
We have declared two header statements [Template in HTML 5, (learning web components)] with styleClass named “pageHeaderStyle“.

index.html | to build the customer registration page

<script src="index.js"></script>
<link rel="stylesheet" href="index.css"></link>
<body onload="loadTmplBody();"> 
<template id="tmplId" >
        <p class="pageHeaderStyle">Template in HTML 5</p>
        <p class="pageHeaderStyle">(learning web components)</p>
        <div id="firstdivId">
           <nice-form header="Customer Registration Form" subHeader="A unique venture company" id="niceformId">

           </nice-form>             
        </div> 
</template>
</body>

Now, we are trying to apply the style for styleClass named “pageHeaderStyle”.

index.css | to change the font color for the header contents

.pageHeaderStyle{
    color:red;
}

.pageHeaderStyle{
    color:green;
}

We have written same styleClass in CSS file. Let’s check what will be the result after execution of the HTML file?

We are keeping the same JS file.

index.js | to activate the template with custom element

function loadTmplBody(){   

    var tmpl = document.querySelector("#tmplId");
    tmpl.content.querySelector("#firstdivId").style = "width:100%; height:800px;background: linear-gradient(to bottom right, #ffffff 0%, #003366 100%);";
    var clonTmpl = tmpl.content.cloneNode(true); 
    document.body.appendChild(clonTmpl);

}
class NiceFormClass extends HTMLElement{
    constructor(){
        super();

        this.innerHTML = `<h1>${this.header}</h1>
                          <h3><i>(${this.subHeader})</i></h3>
                          <hr/>
                          <div style="border-style: solid;
                          border-width: 1px;
                          margin-left:3px;
                          border-color: black;background-color:#ccc; width:99.4%;height:200px;"></div>
                          <hr/>
                         `;

    }

    get header(){
        return this.getAttribute('header');
    }

    set header(val){
        this.setAttribute('header', val);
    }

    get subHeader(){
        return this.getAttribute('subHeader');
    }

    set subHeader(val){
        this.setAttribute('subHeader', val);
    }
    
}

customElements.define('nice-form', NiceFormClass);

Now, run the index.html file and you will get the color of both header contents [Template in HTML 5 and (learning web components)] have been overridden as green color, because, the styles are not encapsulated in the main DOM tree.

Result 1

How can we overcome this problem?
We can think about Shadow DOM. Now we will apply the Shadow DOM for this assignment.

We have modified the HTML to define the ids for one header content.

index.html | to apply the html id for the header content (learning lightning component)

<script src="index.js"></script>
<link rel="stylesheet" href="index.css"></link>
<body onload="loadTmplBody();"> 
<template id="tmplId" >
        <p class="pageHeaderStyle">Template in HTML 5</p>
        <p id="secondPgraphId">(learning web components)</p>
        <div id="firstdivId">
           <nice-form header="Customer Registration Form" subHeader="A unique venture company" id="niceformId">
		   </nice-form>             
        </div> 
</template>
</body>

We are keeping the same CSS file as described earlier. You can remove the duplicate pageHeaderStyle class from CSS file.

index.js | to apply the shadow DOM [Line no.8-13]

function loadTmplBody(){   

    var tmpl = document.querySelector("#tmplId");
    tmpl.content.querySelector("#firstdivId").style = "width:100%; height:800px;background: linear-gradient(to bottom right, #ffffff 0%, #003366 100%);";
    var clonTmpl = tmpl.content.cloneNode(true); 
    document.body.appendChild(clonTmpl);

    let host = document.querySelector('#secondPgraphId');
    let shadowRoot = host.createShadowRoot();
    let newP = document.createElement('p');
    newP.className="pageHeaderStyle1";
    newP.textContent="(learning web components)";
    shadowRoot.appendChild(newP);
}
class NiceFormClass extends HTMLElement{
    constructor(){
        super();

        this.innerHTML = `<h1>${this.header}</h1>
                          <h3><i>(${this.subHeader})</i></h3>
                          <hr/>
                          <div style="border-style: solid;
                          border-width: 1px;
                          margin-left:3px;
                          border-color: black;background-color:#ccc; width:99.4%;height:200px;"></div>
                          <hr/>
                         `;

    }

    get header(){
        return this.getAttribute('header');
    }

    set header(val){
        this.setAttribute('header', val);
    }

    get subHeader(){
        return this.getAttribute('subHeader');
    }

    set subHeader(val){
        this.setAttribute('subHeader', val);
    }
    
}

customElements.define('nice-form', NiceFormClass);

Following syntax has been used to apply Shadow DOM.

    let host = document.querySelector('#secondPgraphId');
    let shadowRoot = host.createShadowRoot();
    let newP = document.createElement('p');
    newP.className="pageHeaderStyle1";
    newP.textContent="(learning web components)";
    shadowRoot.appendChild(newP);

First, we got the DOM element for secondPgraphId by using querySelector and created Shadow Root for a particular Shadow Host. Then we have inserted new DOM into the Shadow DOM by declaration of document properties.

You can apply attachShadowRoot() instead of using createshadowRoot() function to apply Shadow DOM.

Result 2

You can see, now the style of second header content (learning web components) can not be changed due to the presence of Shadow DOM.

Now, if you do the inspect for a particular content in the browser, you can see the Shadow Root definition. Please see the following image.

 

18,378 total views, 6 views today

What is Custom Element?

One of the most important feature in HTML 5 is Custom Element. We can define our own tag with the help of Custom Element. To build the Custom Element, we need to create following components.

1. Define the DOMString name of the Custom Element in HTML file. Remember, Custom Element Name will be separated with hyphen(-). You can set the predefined attributes in your own custom element tag.
2. Define the Class Name which will extend the out of the box feature from HTMLElement.
3. Associate the Class Name with the Custom Element Name.

Let’s we have taken the previous example where we will define the custom element.

Objective: To make the Custom HTML form tag by Custom Element in HTML 5.

Step 1: We have created the Custom Element named “nice-form” with certain attributes such as “header” and “subHeader” in index.html file.

index.html | to define the HTML with custom element named “nice-form”

<script src="index.js"></script>
<body onload="loadTmplBody();">
    <template id="tmplId" >
        Template in HTML 5
        <div id="firstdivId">
            <nice-form header="Customer Registration Form" subHeader="A unique venture company" id="niceformId" >
                 
            </nice-form>
        </div>
    </template>    
</body>

Step 2: After that, we have created the Class and associate the Class with the Custom Element in JS file.

index.js | to define the class for the custom element

function loadTmplBody(){
    var tmpl = document.querySelector("#tmplId");
    tmpl.content.querySelector("#firstdivId").style = "width:100%; height:800px;background: linear-gradient(to bottom right, #ffffff 0%, #003366 100%);";
    var clonTmpl = tmpl.content.cloneNode(true);
    document.body.appendChild(clonTmpl);
}
class NiceFormClass extends HTMLElement{
    constructor(){
        super();

        this.innerHTML = `
<h1>${this.header}</h1>

                          
<h3><i>(${this.subHeader})</i></h3>

                          <hr/>
                          
<div style="border-style: solid; border-width: 1px; margin-left:3px; border-color: black;background-color:#ccc; width:99.4%;height:200px;"></div>

                          <hr/>
                         `;

    }

    get header(){
        return this.getAttribute('header');
    }

    set header(val){
        this.setAttribute('header', val);
    }

    get subHeader(){
        return this.getAttribute('subHeader');
    }

    set subHeader(val){
        this.setAttribute('subHeader', val);
    }
    
}

customElements.define('nice-form', NiceFormClass);

We have created a JS Class named “NiceFormClass” for the custom element “nice-form”. As well as we have defined getter and setter methods for header & subHeader properties. We have declared the innerHTML into the constructor of the class to render the HTML when custom element will be executed.

Result

Advantages of Using Custom Element

You can create own custom tag in HTML along with this you can also create API for your own HTML elements.
Re-usability has been increased due to the use of custom element. You can create common custom element and reuse this custom element in the HTML applications.

9,486 total views, 9 views today

How to use HTML Template as a part of Web Component?

HTML template is a mechanism to hold the client-side content but it’s not rendered during page loading until it’s forcefully instantiated from the JavaScript.

Let’s say, we are going to create html and js files to understand the HTML 5 template feature. To do this exercise, we are using VS Code or you can use any HTML editor available in the market now. For now, just we are going to build a background for a HTML file.

Step 1: We have created a html file called index.html

index.html | to define the template

<template id="tmplId" >
        Template in HTML 5
        <div id="firstdivId">
            
        </div>
</template>

Here, we have defined a template tag in which div tag is declared to set the background color.

Step 2: Run this index.html file to see the output. You will not see any output, because template will not be visible by default. We need to write JS to instantiate the template.

Step 3: We have created a JS file called index.js or you can write the script in the same HTML file.

index.js | to push the template into the main DOM

function loadTmplBody(){
    var tmpl = document.querySelector("#tmplId");
    tmpl.content.querySelector("#firstdivId").style = "width:100%; height:800px;background: linear-gradient(to bottom right, #ffffff 0%, #003366 100%);";
    var clonTmpl = tmpl.content.cloneNode(true);
    document.body.appendChild(clonTmpl);
}

Here, we have declared a JS method called loadTmplBody where we have instantiated template. First, we got the template node by running querySekector using template Id i.e., tmplId. After that, we have made style for this template content. Then, we are cloning the template content and append into the main DOM by document.body.appendChild(clonTmpl).

Step 4: Now, we have updated the index.html file.

index.html | to call the JS function during onload

<script src="index.js"></script>
<body onload="loadTmplBody();">
    <template id="tmplId" >
        Template in HTML 5
        <div id="firstdivId">
            
        </div>
    </template>    
</body>

We have attached the JS file and set the JS method called loadTmplBody() during onload. So, at the time of page loading that method will be invoked and JS function will be used to activate the template.
Result

Please run the index.html file and you will get the following result with good background screen in the browser.

Advantages of using template

By default, template content will not display until it’s instantiated through JavaScript, because it’s not attached with the main DOM until it’s externally pushed to the main DOM by JavaScript. So, at the time of page loading browser will detect the respective JS function to activate the template instead of loading the whole DOM in a single instance. Hence, browser performance has been improved using template.

Notes

Instead of calling the JavaScript method from during the page load, you can create one custom button and during onclick event you can call the respective JavaScript method to activate or instantiate the template.

10,020 total views, 6 views today

Communication Types for Lightning Web Components

Same Type of Communication in Lightning Component exists in Lightning Web Component also. Following are the mechanisms to communicate between Lightning Web Components.

Direct Call Lightning Web Component with certain attributes – Remember this communication is in one direction, not by bi-directional or back & forth. In Lightning Component (in Aura framework), the communication can be bi-directional using bound expressions, that’s why it was very hard to maintain and performance was not good during using of unnecessarily/accidentally bi-directional communication. This is a primary difference between Lightning Component & Lightning Web component with respect to Communication mechanism.

Using Method – We can call the method with parameters of the Lightning Web Component from other Lightning Web Component. The flow of this communication is from Parent to Child Component.

Using Custom Event – We can call the Parent Component from Child Component with some properties. The flow of this communication is from Child to Parent Component.

Using Publish-Subscriber model – This is just like Application Event communication like in Lightning Component (in Aura Framework).

Using Slot – This is the HTML markup communication by which we can send the HTML markup from Parent Component to Child Component where <slot> tag is defined.

First of all, we need to understand the Owner, Container & Containment Hierarchy for Communication Purpose. 

 

Okay, let’s start. We will discuss all the above communication process one by one.

 

 

 

11,067 total views, 6 views today

Web Component in HTML 5

Introduction

Before going to understand the Lightning Web Component, we need to focus on some basic features of Web Component introduced at the part of HTML 5. Web Components provide standard component model for the Web and allow encapsulation of standard DOM Tree in HTML. We can encapsulate and create our own APIs, elements in web component.  Following are the primary features provided by Web Component such as

  • HTML Templates: It’s not rendered by default until it’s instantiated from JavaScript.
  • Custom Elements: Own Tags or APIs can be created to describe new HTML elements.
  • Shadow DOM: Encapsulated the main DOM of the HTML.
  • HTML Import/ Export: Able to import or export any HTML files in the main HTML file.

We will learn one by one in the next sessions.

Why Web Component? 

  • We can reuse the elements in the application by import/export mechanism. That means, Web Component increases re-usability of elements in Web Applications.
  • Web Component depends on modular based. So, maintainability is much easier than normal HTML component.
  • We can define APIs by using Custom Element to enhance the productivity as per business.
  • Web Component can extend the existing browser elements to maintain the browser’s accessibility.
  • You can encapsulate the main DOM tree to avoid the same styling for all similar classes.

Browser Support

Web Components are supported in almost all modern browsers now. However, Web Component is best suited for Google Chrome and Opera browser.

 

Are you excited to learn? right?

Hold on…be prepare with paper and pen…

Let’s start our journey……  

12,136 total views, 18 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