About Me

My photo
PLANO, Texas, United States

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;

   }

}


 

 





No comments:

Post a Comment