Post 1 – Communicate Between Salesforce Lightning Web Components: Child to Parent

This is the latest series of posts I am doing on a new Pluralsight course I created titled, “Communicate Between Salesforce Lightning Web Components”.

Communicating With Events

In this post, I will be covering the different ways nested lightning web components can communicate from the child to the parent using custom events.

Child to Parent Component Relationship

Custom events are used to communicate up the component hierarchy. They allow a child to communicate with it’s parent component.

Even though they are based on a web standard, Lightning Web Components offer a CustomEvent interface to create and dispatch these events.

For simple events, in which no data needs to be passed to the parent component, the custom event can be created and dispatched with a single line of code, such as what you see below:

handleClick() {
	 this.dispatchEvent(new CustomEvent('clicked'));
}

The event type, which in this example is “clicked” is required. It should also follow the DOM event naming standard. This means there should be no uppercase letters or spaces. If two words are used, they should be separated with an underscore.

Child to Parent Example

I am going to show you a nested component scenario and point out the important things to consider. In this scenario the children will not be able to communicate with each other. They will only communicate with their parent component. I will also use Lightning App Builder to create a one-region app page to host this parent component. The end result will look like the following:

Simple Child to Parent Communication
Simple Child to Parent Communication

Parent Component Code

In this example, there will be a parent component that will act as a container component and nested inside of it will be two children components named child and child2.

<!-- parent.html -->

<template>
    <lightning-card 
        title="Child to Parent Communication" 
        icon-name="utility:people">
        <lightning-layout vertical-align="start">
            <lightning-layout-item padding="around-small" size="6">
                <div class="slds-box_small">
                    <b>Event Name:</b>
                    <lightning-formatted-text 
                        class="slds-m-left_small" 
                        value={eventName}>
                    </lightning-formatted-text>
                    <div class="slds-box slds-m-around_small">
                        <c-child onclicked={handleButtonClicked}></c-child>
                     </div>
                </div>
            </lightning-layout-item>
            <lightning-layout-item padding="around-small" size="6">
                <div class="slds-box_small">
                    <b>Event Name:</b>
                    <lightning-formatted-text 
                        class="slds-m-left_small" 
                        value={eventName2}>
                    </lightning-formatted-text>
                    <div class="slds-box slds-m-around_small">
                        <c-child2 onclicked2={handleButtonClicked2}></c-child2>
                    </div>
                </div>
            </lightning-layout-item>
      </lightning-layout>
    </lightning-card>
</template>
//parent.js

import { LightningElement } from 'lwc';

export default class Parent extends LightningElement {
    eventName;
    eventName2;

    handleButtonClicked2(event) {
       //this.eventName2 = 'Child2 Button Clicked: ' + event.detail;
       this.eventName2 = 'Child2 Button Clicked: ' + 
            event.detail.ename + event.detail.num;
    }

    handleButtonClicked(event) {
        this.eventName = 'Child Button Clicked';
    }
    
}
<!-- parent.js.meta.xml-->

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>50.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>LWC Events</masterLabel>
    <description>This is a component demonstrating how child to parent event relationship works</description>
    <targets>
        <target>lightning__AppPage</target>
    </targets>
</LightningComponentBundle>

Simple Child Component

This child component will use an inline event handler to simply pass a notification to the parent that a button has been clicked inside the child. There is not much to say about this method since it is very straight forward.

<!-- child.html -->

<template>
    <lightning-card title="Simple Child Component">
        <lightning-layout vertical-align="start">
            <lightning-layout-item padding="around-small" size="12">
                <lightning-button 
                    variant="brand" 
                    label="Fire Event from Simple Child" 
                    onclick={handleClick} 
                    class="slds-m-around_small">
                </lightning-button> 
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>
// child.js

import { LightningElement } from 'lwc';

export default class Child extends LightningElement {
    
    handleClick() {
        this.dispatchEvent(new CustomEvent('clicked'));
    }

}

Child Component Passing Data

The child2 component will be more complex since it will pass multiple data elements from the child to the parent component. The first input data field can be used to enter a text-based field and the second one allows the user to enter a number. The values for both data fields will be concatenated together and displayed as an event name in the parent component.

<!-- child2.html -->

<template>
    <lightning-card title="Complex Child Component">
        <lightning-layout vertical-align="start">
            <lightning-layout-item padding="around-small" size="12">
                <lightning-input 
                    type="text" 
                    name="inName" 
                    label="Enter a name:" 
                    class="slds-m-around_small"
                    onchange={handleNameChange}>
                </lightning-input>
                <lightning-input 
                    type="number" 
                    name="inNumber" 
                    label="Enter a number:" 
                    class="slds-m-around_small"
                    onchange={handleNumberChange}
                    placeholder="2">
                </lightning-input>
                <lightning-button 
                    variant="brand" 
                    label="Fire Event from Complex Child" 
                    onclick={handleClick} 
                    class="slds-m-around_small">
                </lightning-button> 
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>
// child2.js

import { LightningElement } from 'lwc';

export default class Child2 extends LightningElement {
    eventNumber = 0;
    copiedObject;
    eventObject = {
        num : 0,
        ename: ''
    };

    handleNumberChange(event) {
        this.eventObject.num = event.detail.value;
    } 

    handleNameChange(event) {
        this.eventObject.ename = event.detail.value;
    } 

    handleClick() {
        // Make a shallow copy into new object
        this.copiedObject = Object.assign({}, this.eventObject);
        this.dispatchEvent(
            new CustomEvent('clicked2', { detail: this.copiedObject } 
        ));
        

    }
}

The really important thing to observe in the Javascript controller for child2 is the code in the handleClick function. I am using an object to pass both the number and name value in the CustomEvent.

On the surface this seems simple, but the official Salesforce documentation includes a warning that custom events should only pass primitive data, such as a string or a number and not an object. This is because any listener (which could be a malicious one) can mutate the passed object and change the values.

To avoid this issue, I am making a shallow copy of the eventObject into a new variable named copiedObject. Only the value of the copied object is passed in the detail of the CustomEvent.

If you found this post helpful, you might want to check out my Pluralsight course, “Communicate Between Salesforce Lightning Web Components”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s