Handle Events
There are two ways to listen for an event: declaratively from the component’s HTML template, or programmatically using an imperative JavaScript API. It’s better to listen from the HTML template since it reduces the amount of code you have to write. To handle events, define methods in the component’s JavaScript class.
Attach an Event Listener Declaratively
Declare the listener in markup in the template of the owner component, in this
example, c-parent
.
<!-- parent.html -->
<template>
<c-child onnotification={handleNotification}></c-child>
</template>
Define
the handler function, in this example handleNotification
, in the c-parent
JavaScript
file.
// parent.js
import { LightningElement } from 'lwc';
export default class Parent extends LightningElement {
handleNotification(){
// Code runs when event is received
}
}
Attach an Event Listener Programmatically
Define both the listener and the handler function in the c-parent
JavaScript.
// parent.js
import { LightningElement } from 'lwc';
export default class Parent extends LightningElement {
constructor() {
super();
this.template.addEventListener('notification', this.handleNotification);
}
handleNotification = () => {};
}
If the same listener is added to the same element repeatedly, the browser ignores the duplicates.
If the event listener isn’t removed, you can choose to inline the handleNotification
code in the addEventListener()
call.
this.template.addEventListener('notification', evt => {
console.log('Notification event', evt);
});
addEventListener(eventName,
this.handleNotification.bind(this))
. It’s an anti-pattern because
bind()
returns a new function and thus
the component can’t call removeEventListener()
with the same function instance. Because the
component can’t use the same function instance, the listener creates a memory
leak.There are two syntaxes for adding an event listener. One to add an event listener to an element within a component’s shadow boundary, and one to add an event listener to an element that the template doesn’t own, for example, an element passed into a slot.
To add an event listener to an element within the shadow boundary, use template
.
this.template.addEventListener()
To add an event listener to an element that a template doesn’t own, call addEventListener
directly.
this.addEventListener()
Get a Reference to the Component That Dispatched the Event
To get a reference to the object that dispatched the event, use the Event.target
property, which is part
of the DOM API for events.
Event.target
.
To find examples, search the repo for Event.target
.Event Retargeting
When an event bubbles up the DOM, if it crosses the shadow boundary, the value of
Event.target
changes to match the scope
of the listener. This change is called “event retargeting.” The event is retargeted
so the listener can’t see into the shadow DOM of the component that dispatched the
event. Event retargeting preserves shadow DOM encapsulation.
Let’s look at a simple example.
<!-- myButton.html -->
<template>
<button>{label}</button>
</template>
A click listener on <my-button>
always receives
my-button
as the target, even if the click
happened on the button
element.
Imagine an event is dispatched from a div
element
in the c-todo-item
component. Within the
component’s shadow DOM, Event.target
is
div
. But to a listener on the p
element in the containing c-todo-app
component, the Event.target
is c-todo-item
,
because the p
element can’t see into the c-todo-item
shadow
DOM.
<c-todo-app>
#shadow-root
<div>
<p>Your To Do List</p>
</div>
<c-todo-item>
#shadow-root
<div>
<p>Go to the store</p>
</div>
</c-todo-item>
</c-todo-app>
It’s interesting to note that to a listener on c-todo-item
, the Event.target
is c-todo-item
, not div
, because c-todo-item
is outside
the shadow boundary.
Listen for Changes to Input Fields
To listen for changes from an element in your template that accepts input, such as a
text field (<input>
or <lightning-input>
), use the onchange
event.
<!-- form.html -->
<template>
<input type="text" value={myValue} onchange={handleChange} />
</template>
// form.js
import { LightningElement } from 'lwc';
export default class Form extends LightningElement {
myValue = 'initial value';
handleChange(evt) {
console.log('Current value of the input: ' + evt.target.value);
}
}
In this example, the handleChange()
method
in the JavaScript file is invoked every time the value of the input changes.
The myValue
property represents the value
for the input element. This property value is not updated automatically on
every change.
You might want extra validation of the value the user enters for auto-correction or
restriction of some values as they type. To keep myValue
synchronized with the current value of the input, update
myValue
in the handleChange()
method. The following
auto-corrects the typed value by removing white spaces at the beginning and end of
the string. Use evt.target.value
to get the
current value of the input field.
// form.js
import { LightningElement } from 'lwc';
export default class Form extends LightningElement {
myValue = 'initial value';
handleChange(evt) {
const typedValue = evt.target.value;
const trimmedValue = typedValue.trim(); // trims the value entered by the user
if (typedValue !== trimmedValue) {
evt.target.value = trimmedValue;
}
this.myValue = trimmedValue; // updates the internal state
}
}
This example shows how to reset the input value property to the trimmed value in line
evt.target.value = trimmedValue
. It
also shows how to keep myValue
property in
sync with the normalized value in case the component gets rehydrated (loaded with
new values) in the future.
evt.target
. The example changes the value used
in the template (myValue
) to keep the state
of the component elements in sync. Otherwise, template rehydration detects a
mismatch of the property value while attempting to reconcile the state of the input
element with the state of your component. This mismatch generates a runtime warning,
and you need to adapt your component or JavaScript to keep the integrity of the data
throughout your template.Remove Event Listeners
The framework takes care of managing and cleaning up listeners for you as part of the
component lifecycle. However, if you add a listener to anything else (like the
window
object, the document
object, and so on), you’re responsible
for removing the listener yourself.
To remove the event listeners, use the disconnectedCallback
lifecycle hook.
No comments:
Post a Comment