About Me

My photo
PLANO, Texas, United States

Monday, November 9, 2020

Reactivity in LWC

In your real life, when and how you should react, it really matters. Similarly, when you write the LWC, you should know when the component should react or when it should not react. Sometimes, the LWC framework reacts automatically however, sometimes we need to tell the framework to react.

If a property is altered or changed, our component reacts and re-renders. You can choose if you want your component to react to a property change. If you don’t want then the component won't re-render.


In LWC, there are three types of decorators (A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter.) which can help to decide component reactivity.

  1. @track- private reactive property

  2. @api- public reactive properly 

  3. @wire-reactive service for data

Let's take a look in detail at each decorator.

@track

All primitive fields are reactive. If a field’s value changes and the field is used in a template or in a getter of a property that’s used in a template, the component re-renders, and displays the new value.

Let’s take a very simple example where you input a number and get the square root. In the below example, intNumber is a reactive field as it is used in the getter method.  When I change the input, the square root updates automatically.

squrerootEx.html 


<template>

   <lightning-card title="Square root" icon-name="custom:custom113">

       <div class="slds-m-around_medium">

           <lightning-input type="number"

                            label="Enter Number"

                            value={intNumber}

                            onchange={getsequreroot}></lightning-input>

 

       <p class="slds-var-m-top_medium">

           Square root of {intNumber} is : {result}

       </p>

       </div>

      

   </lightning-card>

</template>


squrerootEx.js


import { LightningElement } from 'lwc';

 

export default class SumEx extends LightningElement {

  intNumber=1;

   getsequreroot(event){

       this.intNumber = event.target.value

   }

   get result(){

       let sqR = Math.sqrt(this.intNumber);

       return sqR;

    }

}


Before Spring ’20, to make the component rerender when a user entered a number, you had to decorate the fields with @track.

@track intNumber;


Only primitive fields are reactive. Object and array type fields are only reactive when you completely set the Object{} or array.


car = {name : '', color: ''}

this.car = {name: 'civic', color: 'blue'}

//component re-renders because the entire object is set again

this.car.name= 'figo'

//component doesn't re-renders because only a property under object is set


To make the component react to properties under an object{} or array, we have to use track 


@track car = {name : '', color: ''}

this.car.name = 'figo'

//component re-renders because we used track

@api

Property in LWC is private by default. To expose a public property, decorate a field with @api. Public properties define the API for a component.

To expose a public method, decorate it with @api. Public methods are part of a component’s API. To communicate down the containment hierarchy, owner and parent components can call JavaScript methods on child components.


Let’s create the two components, first one is a child where we define a property which we want to call it from the parent so we declare as @api as it can be exposed publicly.


childEx.html


<template>

   <div>{itemName}</div>

</template>


childEx.js

import { LightningElement,api } from 'lwc';

 

export default class ChildEx1 extends LightningElement {

   @api itemName;

}


parentEx.html

<template>

   <c-child-ex1 item-name="Milk"></c-child-ex1>

   <c-child-ex1 item-name="Bread"></c-child-ex1>

</template>


parentEx.js

import { LightningElement } from 'lwc';

 

export default class ParentEx1 extends LightningElement {}


In the above example, if you will not write itemName as @api, nothing will be exposed to the parent component.

@wire

wire is literally like a wire which connects LWC with salesforce Data. To read Salesforce data, Lightning web components use a reactive wire service. When the wire service provisions data, the component re-renders. Components use @wire in their JavaScript class to specify a wire adapter or an Apex method.


You can refer to my previous article to understand wire adapters.



Reference

Salesforce Documentation


Sunday, November 8, 2020

Component Communication In LWC

You can add components within the body of another component. Composition enables you to build complex components from simpler building-block components. 

Composing apps and components from a set of smaller components make code more reusable and maintainable.

Owner Vs Container

Owner

The owner is the component that owns the template.  An owner can:

  • Set public properties on composed components

  • Call public methods on composed components

  • Listen for events dispatched by composed components

Container

A container contains other components but itself is contained within the owner component. A container is less powerful than the owner. A container can:

  • Read, but not change, public properties in contained components

  • Call public methods on composed components

  • Listen for some, but not necessarily all, events bubbled up by components that it contains. 


Parent Vs child

When a component contains another component, which, in turn, can contain other components, we have a containment hierarchy. In the documentation, we sometimes talk about parent and child components. A parent component contains a child component. A parent component can be the owner or a container.


Component Communication In LWC


Now let’s understand how components communicate in LWC. Since we know that lwc components can be nested there are 3 possibilities for communication between components.

  • Parent to child communication

  • Child to parent communication

  • Communication between two separate components.

Parent to child communication

Public property of any child can be accessed through parent components. Use @api decorator to make the field public. Let’s understand all of this with a couple of examples.

Example 1:

Let’s start with a very basic example where the parent component will access child property.  First, create the child component (Container) childEx1 which will be used in parentEx1 component.


childEx1.html


<template>

   <div>{itemName}</div>

</template>



childEx1.js


The @api decorator exposes the itemName field as a public property.


import { LightningElement,api } from 'lwc';

 

export default class TodoApp extends LightningElement {

   @api itemName;

}


Now let’s create parent or owner components that will call child components.

parentEx1.html


<template>

   <c-child-ex1 item-name="Milk"></c-child-ex1>

   <c-child-ex1 item-name="Bread"></c-child-ex1>

</template>



You notice here while calling the child component, Property names in JavaScript are in camel case while HTML attribute names are in kebab case (dash-separated) to match HTML standards.


parentEx1.js

Keep default js file

import { LightningElement } from 'lwc';

 

export default class ParentEx1 extends LightningElement {}


Make the required changes in your metafile of component and add your component to your page


Example 2:

Let's find out other example, there are two component childEx2 and parentEx2 as below:


childEx2.html

<template>

   <template if:true={contact}>

       <img src={contact.Picture} alt="Profile photo" />

       <p>{contact.Name}</p>

       <p>{contact.Title}</p>

     </template>

     <template if:false={contact}>

       <p>No contact data available.</p>

     </template>

</template>



childEx2.js


import { api, LightningElement } from 'lwc';

 

export default class ChildEx2 extends LightningElement {

   @api contact;

}


parentEx2.html


<template>

   <c-child-ex2 contact={contact}></c-child-ex2>

</template>


parentEx2.js


import { LightningElement } from 'lwc';

 

export default class ParentEx2 extends LightningElement {

   contact={  

       Name: 'Amy Taylor',

       Title: 'VP of Engineering',

       Picture: 'https://s3-us-west-1.amazonaws.com/sfdc-demo/people/amy_taylor.jpg',

   };

}


In both the above examples, we understand how property can set from Parent to child. Let's go with a more realistic example where the parent component will call the method from the child. 

To expose a public method, decorate it with @api. Public methods are part of a component’s API. To communicate down the containment hierarchy, owner and parent components can call JavaScript methods (or set properties) on child components.


Example 3:

Consider we have a component called parentEx3 which has a date input asking for your birth date.. Also inside this parentEx3 component, we have another component called childEx3 component which shows a message about your birthday, whenever you select a birthdate in parentEx. 


childEx3.html


<template>

 

   <lightning-card title='Child Component'>

       {greetings}

   </lightning-card>

 

</template>


childEx3.js


import { api, LightningElement } from 'lwc';

 

export default class ChildEx3 extends LightningElement {

   greetings;

   @api

   handleBday(bdayDate){

       this.greetings = 'Hey There Your Bday On ' + bdayDate;

   }

}


parentEx3.html


<template>

   <lightning-card title='Parent Card'>

       <lightning-input type="date" name="input1" label="Select Your Birthdate" onchange={passBday}></lightning-input>

   </lightning-card>

   <lightning-card title='Parent call child'>

       <c-child-ex3></c-child-ex3>

   </lightning-card>

</template>

parentEx3.js


import { LightningElement} from 'lwc';

 

export default class ParentEx3 extends LightningElement {

   passBday(event){

       console.log(event.target.value);

       this.template.querySelector('c-child-ex3').handleBday(event.target.value);

   }

}


Example 4:

Let’s take another example where Parents will call a child component. In this example, the parent will call the child component method which will refresh the clock.

Child Component-

childEx4.html


<template>

   <lightning-formatted-date-time

       value={timestamp}

       year="numeric"

       month="numeric"

       day="numeric"

       hour="2-digit"

       minute="2-digit"

       second="2-digit"

       time-zone-name="short"

   >

   </lightning-formatted-date-time>

</template>


childEx4.js


import { api, LightningElement } from 'lwc';

 

export default class ChildEx4 extends LightningElement {

   timestamp = new Date();

   @api

   refresh(){

       console.log('This is refresh');

       this.timestamp=new Date();

   }

 

}


Parent Component-

parentEx4.html


<template>

   <lightning-card>

   <lightning-button variant="brand"

                     label="Brand"

                     title="Refresh Talk"

                     onclick={handleClick}

                     class="slds-m-left_x-small"></lightning-button>

  

   <c-child-ex4></c-child-ex4>

   </lightning-card>

</template>


parentEx4.js


import { LightningElement } from 'lwc';

 

export default class ParentEx4 extends LightningElement {

   handleClick(){

       console.log('this is handleClick');

       this.template.querySelector('c-child-ex4').refresh();

   }

 

}



So points to note down for Parent to child communication is:

  • The child must have public property decorated with @api

  • To call the child method can use this.template.querySelector

Child to Parent communication

Now let’s understand how the child LWC communicates to the parent LWC. In order to make a communication, create and dispatch the custom event from the child component and listen from the parent.

Create & Dispatch an Event on Child LWC

  1. Create Event 

We can use the customEvent() constructor to create an event. The CustomEvent() constructor has one required parameter, which is a string indicating the event type. As a component author, you name the event type when you create the event. You can use any string as your event type. However, Salesforce recommends that conform with the DOM event standard.

  • No uppercase letters

  • No spaces

  • Use underscores to separate words

 

Syntax
new customEvent(eventName, props);

 

Note-Don’t prefix your event name with the string on, because inline event handler names must start with the string on. If your event is called onmessage, the markup would be <c-my-component ononmessage={handleMessage}>. Notice the doubled word onon, which is confusing.

  1. Dispatch Event 

We have to dispatch an event with EventTarget.dispatchEvent() method.
Syntax

this.dispatchEvent(new customEvent(eventName, props);

Handle an Event on Parent LWC

There are two ways to listen to an event

  1. Declarative via html markup : We need to add an “on” prefix in the event name in Parent Component during calling of Child Component for Declaratively event listener.
    ParentComponent
    <template>
          <c-child-component oneventName={listenerHandler}></c-child-component >
    </template>

  2. JavaScript using addEventListener method : We can explicitly attach a listener by using the addEventListner method in the parent component like below :
    ChildComponent
    this.template.addEventListener('eventName', this.handleNotification.bind(this));

Example

childEx4.html

The c-childEx4 component contains Previous and Next buttons. When a user clicks the buttons, the component creates and dispatches previous and next events. You can drop the paginator component into any component that needs Previous and Next buttons. That parent component listens for the events and handles them.


<template>

   <lightning-layout>

       <lightning-layout-item>

           <lightning-button

               label="Previous"

               icon-name="utility:chevronleft"

               onclick={handlePrevious}

           ></lightning-button>

       </lightning-layout-item>

       <lightning-layout-item flexibility="grow"></lightning-layout-item>

       <lightning-layout-item>

           <lightning-button

               label="Next"

               icon-name="utility:chevronright"

               icon-position="right"

               onclick={handleNext}

           ></lightning-button>

       </lightning-layout-item>

   </lightning-layout>

</template>

 

childEx4.js

When a user clicks a button, the previousHandler or nextHandler function executes. These functions create and dispatch the previous and next events.


import { LightningElement } from 'lwc';

 

export default class Paginator extends LightningElement {

   handlePrevious() {

       this.dispatchEvent(new CustomEvent('previous'));

   }

 

   handleNext() {

       this.dispatchEvent(new CustomEvent('next'));

   }

}

 

These events are simple “something happened” events. They don’t pass a data payload up the DOM tree, they simply announce that a user clicked a button.

Let’s drop childEx5  into a component called c-parent-ex5, which listens for and handles the previous and next events.

parentEx5.html

To listen for events, use an HTML attribute with the syntax oneventtype. Since our event types are previous and next, the listeners are onprevious and onnext.


<template>

   <lightning-card title="Pagination">

          Page {page}

           <c-child-ex5 onprevious={previousHandler1}

                        onnext={nextHandler1}></c-child-ex5>

      

   </lightning-card>

</template>

 

When c-parent-ex5 receives the previous and next events, previousHandler1 and nextHandler1 increase and decrease the page number. Here you notice, method name is not necessary to have the same name as child component.


import { LightningElement } from 'lwc';

 

export default class ParentEx5 extends LightningElement {

   page = 1;

 

   previousHandler1() {

       console.log('This is previousHandler1 in parent component');

       if (this.page > 1) {

           this.page = this.page - 1;

       }

   }

 

   nextHandler1() {

       console.log('This is nextHandler1 in parent component');

       this.page = this.page + 1;

   }

}