Lightning web components dispatch standard DOM events. Components can also create and dispatch custom events. Events in Lightning web components are built on DOM Events, a collection of APIs and objects available in every browser. To communicate up the component containment hierarchy in Lightning Web Components, we have to implement the custom events.
Creating an event takes following programming design pattern:
- An event name, called a type
- A configuration to initialize the event
- A JavaScript object that emits the event
- No uppercase letters are allowed
- No spaces are allowed
- Use underscores to separate words
Component Hierarchy |
Now suppose the Grand Child wants to notify of its behavior change to its parents in the hierarchy. What are the possible ways.
-
Simply Grand child fires an event and it will be listened by the parents in the components markup hierarchy - Answer to this is BIG NO Wondered why?
LWC is all about shadow DOM and one can't simply cross/leak through DOM unless otherwise is authorized to do so.
So how to create and dispatch event from Grand Child which will propagate through the DOM up to its root i.e. Grand Parent? Let's crack on it.
- Configure Event Propagation - Once an event is fired by Grand Child, it can propagate up through the DOM but when and where this event can be handled, we have to understand the event propagation.
- bubbles: Indicates whether an event can bubble up the DOM or not defaulted to false.
- composed: Indicates whether an event can pass through the SHADOW boundary or not defaulted to false.
<template>
<lightning-layout class="slds-var-m-around_medium">
<lightning-layout-item class="wide">
You're in grand child component.
<lightning-button label="Hit Me On Grand Child" variant="brand" onclick={handleClick}></lightning-button>
<br /><span class="slds-text-color_success">{text}</span>
</lightning-layout-item>
</lightning-layout>
</template>
import { LightningElement } from 'lwc';
export default class GrandChild extends LightningElement {
text;
handleClick(event) {
event.preventDefault();
this.text = 'Notify event fired in grand child.';
// Creates the event
const customEvent = new CustomEvent('notify');
// Dispatches the event.
console.log(this.text);
this.dispatchEvent(customEvent);
}
}
<template>
<lightning-layout class="slds-var-m-around_medium">
<lightning-layout-item class="wide">
You're in child component.
<c-grand-child onnotify={handleNotify}></c-grand-child>
<span class="slds-text-color_success">{text}</span>
</lightning-layout-item>
</lightning-layout>
</template>
import { LightningElement } from 'lwc';
export default class Child extends LightningElement {
text;
handleNotify(event) {
event.preventDefault();
this.text = 'Notify event listened in child.';
console.log(this.text);
}
}
<template>
<lightning-layout class="slds-var-m-around_medium">
<lightning-layout-item class="wide">
You're in parent component.
<c-grand-child onnotify={handleNotify}></c-grand-child>
<span class="slds-text-color_success">{text}</span>
</lightning-layout-item>
</lightning-layout>
</template>
import { LightningElement } from 'lwc';
export default class Parent extends LightningElement {
text;
handleNotify(event) {
event.preventDefault();
this.text = 'Notify event listened in parent.';
console.log(this.text);
}
}
<template>
<lightning-layout class="slds-var-m-around_medium">
<lightning-layout-item class="wide">
You're in grand parent component.
<c-grand-child onnotify={handleNotify}></c-grand-child>
<span class="slds-text-color_success">{text}</span>
</lightning-layout-item>
</lightning-layout>
</template>
import { LightningElement } from 'lwc';
export default class GrandParent extends LightningElement {
text;
handleNotify(event) {
event.preventDefault();
this.text = 'Notify event listened in grand parent.';
console.log(this.text);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>51.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
-
bubbles:false and composed:false - This is same as firing event
without these event properties since they are defaulted to false by
component framework. Just look at grandChild.js file and firing of
event.
which is same asconst customEvent = new CustomEvent('notify');
Now when you click on Hit Me On Grand Child button on Grand Child component, see the output Event is stopped to bubble up higher because of SHADOW DOM boundary. It can't go past the immediate container and hence Child is the ultimate destination where the event has stopped its propagation.const customEvent = new CustomEvent('notify', { bubbles: false, composed: false });
-
bubbles:true and composed:false -
Here again since the bubbles property is set to true but composed property, which is responsible for an event to cross the SHADOW Boundary, is set to false so we don't see much change in the behavior and output is same as above.const customEvent = new CustomEvent('notify', { bubbles: true, composed: false });
-
bubbles:true and composed:true -
Here since bubbles property is set to true which allows the event to bubble up the hierarchy and setting composed property to true makes the event accessible outside the SHADOW DOM but with the concept of event retargeting as discussed under Configure Event Propagation section. Look at the output now. The event notify has crossed the SHADOW Boundary and reached to its root i.e. Grand Parent in the containment hierarchy.const customEvent = new CustomEvent('notify', { bubbles: true, composed: true });
handleNotify(event) {
event.preventDefault();
this.text = 'Notify event listened in parent.';
console.log(this.text);
event.stopPropagation();// stops the propagation here and event can't go up the hierarchy
}
The event has stopped its propagation beyond the Parent and thus the Grand Parent is no longer able to listen to this notify event.
If you like this blog content and find inciteful, please comment and let me know.
Great blog! Very informative.
ReplyDeleteIf I may, I would like to ask a question.
I understand that we use event.detail when we are listening to this notify event from a child or up the hierarchy. When do we use event.target? Could you explain this from the above example?
Sure, Thanks for asking this question. I will explain the event.target and event.detail in my upcoming blog.
DeleteThank you so much.
ReplyDeletewe use Event.Target to get the access or reference of the component who has fired that event. As it is already mentioned in blog because of event retargeting it will give the reference of only 'c-grand-child' not the 'lightning-button' to preserve the encapsulation. so in parent if you console the event.target.value it will print 'c-grand-child'.
ReplyDeleteAmazing blog and the gem in crown is the example which explained it so easily when its theory seemed mind boggling. Can you also explain Lightning Messaging Service ?
ReplyDeleteThanks for the feedback. I will definitely cover Lightning Messaging Service in my upcoming blog.
DeleteYou can now check out my new blog as requested by you on Lightning Message Service
ReplyDeletehttps://inevitableyogendra.blogspot.com/2021/06/communicate-across-dom-with-lightning-message-service.html
Hope this will be equally helpful in your learning curve.
while executing your code, getting the error, pls advise.
ReplyDeleteAction.prototype.finishAction Error [Error in $A.getCallback() [LightningElement is not defined]
Callback failed: serviceComponent://flexipage.editor.aura.component.FlexipageComponentController/ACTION$loadComponentDefinitions]
new Aura.externalLibraries()@https://playful-hawk-5k9krt-dev-ed.lightning.force.com/auraFW/javascript/7FPkrq_-upw5gdD4giTZpg/aura_prod.js:354:407
Amazing blog and very profitable. thanks.
ReplyDeletei have a few doubts :
ReplyDelete1. If the properties are set to bubble : false n composed : false but still its crossing the shadow DOM and here child can react to the event triggered in grandchild. But as u have said if composed : false then how its crossing the shadow DOM.
2. whats the use of bubble : true and composed : false as its showing the same result as bubble : false n composed : false. Pease cite an real time example .
3. So when a child component is wrapped by the parent component . Then only the shadow DOM gets created or is there any way by which shadom DOM can be created into the component itself.
4. As you are calling the grandchild component in child and the granchild component in parent as well as in grand parent. So i this manner the child , parent and grandparent would be siblings because if grandchild is called in child , child called into parent and parent called into child the it would have formed a hierarchy.
Please clear my doubts thanks in advance.
i have a few doubts :
ReplyDelete1. If the properties are set to bubble : false n composed : false but still its crossing the shadow DOM and here child can react to the event triggered in grandchild. But as u have said if composed : false then how its crossing the shadow DOM. Its contadicting the statement.
2. whats the use of bubble : true and composed : false as its showing the same result as bubble : false n composed : false. Please cite an real time example .
3. So when a child component is wrapped by the parent component . Then only the shadow DOM gets created or is there any way by which shadom DOM can be created into a component itself.
4. As you are calling the grandchild component in child and the granchild component in parent as well as in grand parent. So i this manner the child , parent and grandparent would be siblings because if grandchild is called in child , child called into parent and parent called into grand parent then it would have formed a hierarchy.
Please clear my doubts thanks in advance.
This comment has been removed by the author.
Deletepegulsfig-hiYonkers Mike Mejia https://www.synergyphysiopilates.ca/profile/Cyberplanet-62-Premium-Full-Crack-Fixed/profile
ReplyDeleteemerenpref