Gestionar eventos en Componentes Web Lightning.
Objetivos de aprendizaje
Después de completar esta unidad, podrá:
- Crear una aplicación que incluya múltiples componentes.
- Describir la estructura de archivos de un componente complejo.
- Gestionar eventos.
Seguir la trayectoria de un evento
Ya construyó un componente y lo envió a una organización. Empecemos ahora a agregar algunas interacciones con la gestión de eventos. Seguimos la trayectoria de un evento a través de varios componentes para gestionar un evento sofisticado en una aplicación. Esta aplicación es un selector de productos para una tienda de bicicletas. Los usuarios hacen clic en el nombre y la imagen de una imagen para ver los detalles.
En esta aplicación hay cuatro componentes:
-
tile (mosaico): muestra un elemento individual.
-
list (lista): ordena los mosaicos.
-
detail (detalles): muestra detalles de los elementos cuando se hace clic en un mosaico (parecido al bikeCard que creó hace un momento).
-
selector: contiene el conjunto de complementos completo. No es necesario utilizar un componente contenedor, aunque en este caso sí que lo utilizaremos para facilitar la gestión de eventos.
Por ahora, la aplicación utiliza un archivo de datos para cargar datos estáticos para hacer pruebas. En la siguiente unidad aprenderá a obtener datos dinámicos de una organización.
Composición de componentes
Vamos a agregar varios archivos a nuestro proyecto para implementarlos en una organización.
- Descargue los archivos para esta aplicación desde aquí: Aplicación de selector de bicicletas para Trailhead.
- Descomprima los archivos en la carpeta force-app/main/default/ lwc del proyecto bikeCard.
Relaciones entre los componentes
En esta aplicación, hay múltiples componentes funcionando juntos; y algunos están anidados dentro de otros. Del mismo modo que se anidan unos elementos HTML dentro de otros, se pueden anidar unos componentes web Lightning —que son elementos HTML personalizados— dentro de otros.
En nuestro sistema de archivos, las carpetas de los componentes no nos dicen mucho sobre las relaciones que existen entre ellos.
Veamos ahora en un diagrama cómo están anidados los componentes en el nivel de la interfaz de usuario.
Si observa los archivos, puede ver que el componente del selector crea la página y representa los componentes de lista(c-list
) y detalle(c-detail
).
<template> <div class="wrapper"> <header class="header">Select a Bike</header> <section class="content"> <div class="columns"> <main class="main" > <c-list onproductselected={handleProductSelected}></c-list> </main> <aside class="sidebar-second"> <c-detail product-id={selectedProductId}></c-detail> </aside> </div> </section> </div> </template>
Actualice detail.html con lo siguiente:
<template> <template lwc:if={product}> <div class="container"> <div>{product.fields.Name.value}</div> <div class="price">{product.fields.MSRP__c.displayValue}</div> <div class="description">{product.fields.Description__c.value}</div> <img class="product-img" src={product.fields.Picture_URL__c.value} alt={product.fields.Name.value}/> <p> <lightning-badge label={product.fields.Material__c.value}></lightning-badge> <lightning-badge label={product.fields.Level__c.value}></lightning-badge> </p> <p> <lightning-badge label={product.fields.Category__c.value}></lightning-badge> </p> </div> </template> <template lwc:else> <div>Select a bike</div> </template> </template>
En detail.html, puede ver la representación condicional (lwc:if={product}
y lwc:else
). Si no seleccionó ningún elemento de la lista, aparecerá un mensaje en el que se pide al usuario que seleccione uno. Cuando se elija una bicicleta, se mostrará la información sobre ella.
El componente de lista muestra varios componentes de mosaico(c-tile
), uno por cada bicicleta que haya en los datos. Este anidamiento se hace en el HTML de cada componente principal. Por ejemplo, el componente de la lista tiene el siguiente HTML, que incluye el componente de mosaico como c-tile
.
<template> <div class="container"> <template for:each={bikes} for:item="bike"> <c-tile key={bike.fields.Id.value} product={bike} ontileclick={handleTileClick}></c-tile> </template> </div> </template>
Observe cómo cada iteración del elemento bicicleta genera un nuevo componente de mosaico. Basta con incluir la etiqueta del componente c-tile
para crear un componente secundario en cada componente de mosaico. Para definir el estilo se utiliza el "contenedor" de definición de clase div, con lo que puede controlar la disposición de los mosaicos. En list.css verá que encapsula el contenido.
.container { display: flex; flex-direction: row; flex-wrap: wrap; }
La relación principal-secundario es importante, no solo para el diseño de la aplicación, sino también para la gestión de eventos.
Adentrémonos un poco más en la gestión de eventos.
Comunicación de eventos hacia arriba, comunicación de propiedades hacia abajo
En un componente complejo (un componente que contenga varios componentes principales y secundarios), los componentes pueden comunicarse hacia arriba y hacia abajo.
- El componente secundario c-todo-item envía un evento al componente principal c-todo-app. Por ejemplo, el componente secundario puede pasar un objeto de evento al componente principal cuando un usuario hace clic en un botón para que el principal pueda gestionar el evento y cambiar la página actual.
- El componente principal c-todo-app pasa una propiedad o invoca un método en el componente secundario. Por ejemplo, el componente principal puede configurar un valor de texto en un componente secundario o invocar un método en el componente secundario.
Veamos cómo funciona esta comunicación.
Pasar información hacia arriba
Se puede pasar información hacia arriba utilizando eventos y procesos de escucha de eventos.
El componente secundario envía el evento y el componente principal lo escucha. Al enviar el evento, se crea un objeto de evento que el componente secundario puede pasar al principal. El componente principal tiene un gestor para responder al evento.
Por ejemplo (no cree estos componentes), un componente secundario como este contiene un método nextHandler()
que crea un objeto de evento simple mediante CustomEvent()
y envía el tipo de evento "next" (siguiente) cuando el usuario hace clic en el botón Next (Siguiente).
// todoItem.js import { LightningElement } from 'lwc'; ... nextHandler() { this.dispatchEvent(new CustomEvent('next')); } }
El componente principal escucha el evento con el controlador de eventos en línea con el prefijo 'on'(onnext).
<!-- todoApp.html --> <template> <c-todo-item onnext={nextHandler}></c-todo-item> </template>
Y pasa el objeto de evento a un gestor de eventos.
// todoApp.js import { LightningElement } from 'lwc'; export default class TodoApp extends LightningElement { ... nextHandler(){ this.page = this.page + 1; } }
Pasar información hacia abajo
Se puede pasar información hacia abajo utilizando propiedades públicas y métodos públicos.
Puede hacer que una propiedad de un componente sea pública colocándole delante el decorador @api
. A continuación, establezca la propiedad pública por un componente externo.
Por ejemplo (no cree estos componentes), si el componente secundario c-todo-item contiene lo siguiente:
// todoItem.js import { LightningElement, api } from 'lwc'; export default class TodoItem extends LightningElement { @api itemName; }
Configure el valor del componente principal con lo siguiente:
<!-- todoApp.html --> <template> <c-todo-item item-name="Milk"></c-todo-item> </template>
Observe que la variable itemName
se establece con el atributo en Kebab case item-name
. Los nombres de propiedades en JavaScript están en Camel Case, mientras que los nombres de atributos HTML están en Kebab Case (separados-con-guiones) para que coincidan con los estándares HTML. El atributo item-name
en el marcado se corresponde con la propiedad de JavaScript itemName
.
Las propiedades públicas son excelentes soluciones para pasar hacia abajo valores primitivos, objetos sencillos y matrices.
También puede utilizar métodos de obtención y reguladores para ejecutar cierta lógica cuando las propiedades son obtener o establecer. Y recuerde que deben estar precedidas por el decorador @api
para que sean públicas para otros componentes.
También puede crear métodos públicos a los que se puede llamar desde un componente principal. Cree un método público en el componente secundario definiéndolo con el decorador @api
y, a continuación, llámelo desde el componente principal.
Supongamos que tenemos un componente secundario como este (no cree estos componentes).
// videoPlayer.js import { LightningElement, api } from 'lwc'; export default class VideoPlayer extends LightningElement { @api play() { // Play music! } }
Cuando el componente c-video-player se incluye en un componente principal, podemos invocar el método desde el componente principal así:
// methodCaller.js import { LightningElement } from 'lwc'; export default class MethodCaller extends LightningElement { handlePlay() { this.template.querySelector('c-video-player').play(); } }
Definimos un método handlePlay()
que desencadena el evento. Entonces utilizamos el método DOM querySelector()
para buscar un elemento DOM denominado c-video-player e invocamos su método público.
Gestionar eventos en HTML
Nuestra aplicación de selector tiene que gestionar un tipo de evento: el que un usuario haga clic en un mosaico. Cuando esto ocurre, el componente de detalles debería volver a representarse con la información sobre el mosaico en cuestión. Puede gestionar eventos en HTML (agregar un proceso de escucha de eventos en la plantilla) o en JavaScript (programar una función de proceso de escucha de eventos). Recomendamos utilizar la opción con HTML, tal como indicamos a continuación.
Cada componente de mosaico escucha el clic del usuario porque el HTML del componente de mosaico (tile.html) contiene un proceso de escucha de eventos onclick
.
<template> <div class="container"> <a onclick={tileClick}> <div class="title">{product.fields.Name.value}</div> <img class="product-img" src={product.fields.Picture_URL__c.value} alt={product.fields.Name.value}/> </a> </div> </template>
Cuando un usuario hace clic en una de las instancias de mosaico en la interfaz de usuario, el proceso de escucha de onclick
llama a la función de gestor tileClick
en el archivo JavaScript tile.js.
import { LightningElement, api } from 'lwc'; export default class Tile extends LightningElement { @api product; tileClick() { const event = new CustomEvent('tileclick', { // detail contains only primitives detail: this.product.fields.Id.value }); // Fire the event from c-tile this.dispatchEvent(event); } }
Patrón de eventos de la aplicación del selector
En nuestra aplicación del selector de productos utilizamos un componente complejo (uno que contiene varios componentes principales y secundarios). Recomendamos que propague el evento hacia arriba a través de la jerarquía de componentes, de forma que los componentes principales puedan responder a eventos secundarios. Si tiene otros componentes secundarios (no el que desencadena el evento), puede pasar una propiedad hacia abajo a los componentes secundarios en respuesta al evento.
El patrón tiene este aspecto:
Para hacer esto, debemos encadenar procesos de escucha de eventos y gestores hacia arriba, hasta el componente de bicicletas eléctricas. A continuación, hay que pasar una propiedad hacia abajo al componente de detalles.
En nuestros archivos, verá lo siguiente.
- tile.html tiene el proceso de escucha de eventos
onclick
que llama al controladortileClick
.
- tile.js tiene el método
tileClick
que crea un nuevoCustomEvent
con el tipo de eventotileclick
y un objeto que contiene un valor dedetail
(detalle) (this.product.fields.Id.value
).
- list.html tiene el proceso de escucha de eventos
ontileclick
que llama al controladorhandleTileClick
.
- list.js tiene el método
handleTileClick
que pasa el evento (evt
) para crear otroCustomEvent
(productselected
) con un objeto que también contiene un valor dedetail
(detalle)evt.detail
. Y envía el evento en JavaScript:// Fire the event from c-list this.dispatchEvent(event);
- selector.html tiene el proceso de escucha de eventos
onproductselected
que llama al controladorhandleProductSelected
.
- selector.js tiene el método
handleProductSelected
establecido enselectedProductId
en el valorevt.detail
pasado en él. La variable "selectedProductId" se pasa del componente del selector al componente de detalles en selector.html:product-id={selectedProductId}
.
- detail.html tiene una directiva condicional (¿recuerda las directivas condicionales de la Unidad 2?) que espera un valor de producto:
<template lwc:if={product}>
- detail.js combina las distintas partes. Crea una variable privada
_productId
para hacer el seguimiento del estado del valorproductId
. A continuación, utiliza un patrón get/set para obtener el valor y establecerlo en una variableproduct
que deja que detail.html cargue el contenido condicional.
Los métodos reguladores y de obtención son una construcción habitual de JavaScript. Le permiten añadir lógica y condiciones a las asignaciones de propiedades.
import { LightningElement, api } from 'lwc'; import { bikes } from 'c/data'; export default class Detail extends LightningElement { product; // Private var to track @api productId _productId = undefined; // Use set and get to process the value every time it's // requested while switching between products set productId(value) { this._productId = value; this.product = bikes.find(bike => bike.fields.Id.value === value); } // getter for productId @api get productId(){ return this._productId; } }
El proceso se repite cada vez que haga clic en un mosaico.
Implementar sus archivos en su organización
Implementemos estos nuevos archivos del proyecto bikeCard en su organización para ver su funcionamiento. Repita los mismos pasos que realizó en la unidad anterior para implementar sus nuevos archivos, abra la organización y cree una página en el Generador de aplicación Lightning con esta aplicación.
- En el proyecto bikeCard en VS Code, haga clic con el botón secundario en la carpeta force-app/main/default y seleccione SFDX: Implementar fuente en organización.
- Desde la paleta de comandos en VS Code, use SFDX: Abrir organización predeterminada para abrir su organización.
- Cree una página con una única región utilizando el componente del selector.
- Asígnele la etiqueta
Your Bike Selection
(Su selección de bicicletas).
- Arrastre su componente selector a la parte superior del diseño de página.
- Guárdelo y actívelo para todos los usuarios.
- Ábralo y vea cómo funciona su componente en la interfaz de usuario.
Ya tiene una página completamente interactiva formada por varios componentes que funcionan juntos. A continuación, vamos a experimentar con los estilos y con la obtención de datos en vivo desde una organización.
Recursos
- Guía del desarrollador de componentes web Lightning: Shadow DOM
- Guía del desarrollador de componentes web Lightning: Comunicar con eventos
- Guía del desarrollador de componentes web Lightning: Crear métodos reguladores y de obtención