Skip to main content

Gestionar eventos en Lightning Web Components

Objetivos de aprendizaje

Después de completar esta unidad, podrá:

  • Crear una aplicación que incluya varios componentes.
  • Describir la estructura de archivos de un componente complejo.
  • Gestionar eventos.

Seguir el recorrido de un evento

Ha creado un componente y lo ha integrado en una organización. Vamos a empezar a agregar opciones de interactividad con la gestión de eventos. Seguimos el recorrido de un evento mediante varios componentes para gestionar eventos de forma sofisticada en una aplicación. Esta aplicación es un selector de productos de una tienda de bicicletas. Los usuarios hacen clic en el nombre y la imagen de una bicicleta para ver más información.

Los componentes del selector de bicicletas.

Esta aplicación tiene cuatro componentes.

  1. tile (icono): muestra un artículo individual.
  2. list (lista): enumera los distintos iconos.
  3. detail (detalles): muestra detalles de un artículo cuando se selecciona un icono (de forma similar a la bikeCard que acaba de crear).
  4. selector (selector): contiene todo el conjunto de componentes. No es necesario usar un componente de contenedor, pero aquí usamos uno para que ayude con la gestión de eventos.

Por ahora, la aplicación usa un archivo de datos para cargar datos estáticos con fines de pruebas. En la siguiente unidad, aprenderá a extraer datos dinámicos de una organización.

Composición de componentes

Vamos a agregar algunos archivos a nuestro proyecto, que podremos implementar más tarde en una organización.

  1. Descargue los archivos para esta aplicación aquí: Aplicación Bike Selector para Trailhead.
  2. Descomprima los archivos en la carpeta force-app/main/default/lwc del proyecto bikeCard. Estructura de archivos de la aplicación Bike Selector.

Relaciones entre componentes

En esta aplicación, se combinan varios componentes, algunos de los cuales se encuentra anidados dentro de otros. Al igual que anida elementos HTML dentro de otros, puede anidar componentes web Lightning (que son elementos HTML personalizados) dentro de otros componentes web Lightning.

En nuestro sistema de archivos, las carpetas de componentes no muestran las relaciones entre ellos.

Veamos cómo se anidan los componentes a nivel de interfaz de usuario en el siguiente diagrama.

Relaciones entre componentes principales/secundarios de la aplicación Bike Selector.

Si mira los archivos, verá que el componente del selector organiza la página y representa los componentes list(c-list) y detail(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 el archivo 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}></img>

      <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>

Si observa detail.html, verá el condicional rendering(lwc:if={product} and lwc:else). Si no se ha elegido nada en la lista, lo que aparece es un mensaje que pida al usuario que haga una selección. Si se ha elegido un producto, se muestra la información de la bicicleta.

El componente de lista reproduce varios componentes de tipo tile(c-tile), uno por cada bicicleta de los datos. Esta anidación tiene lugar en el archivo HTML de cada componente principal. Por ejemplo, el componente list (lista) tiene el siguiente código HTML, que incluye el componente tile (icono) 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 de artículo de bicicleta genera un nuevo componente tile (icono). El simple hecho de incluir la etiqueta de componente c-tile hace que cada componente tile (icono) sea su elemento secundario. La definición de clase div "container" se utiliza para aplicar estilo, de forma que pueda controlar la disposición de los iconos. Si abre el archivo list.css, verá que resume todo 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 para la gestión de eventos.

Profundicemos un poco más en la gestión de eventos

Comunicación ascendente de eventos y descendente de propiedades

En un componente complejo (uno que contenga varios componentes principales y secundarios), los componentes pueden comunicarse de forma ascendente o descendente.

Componentes principales y secundarios pasando información de forma ascendente y descendente.

  1. 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 principal cuando un usuario hace clic en un botón para que el principal pueda gestionar el evento y cambiar la página actual.
  2. El componente principal c-todo-app pasa una propiedad o invoca un método en el componente secundario. Por ejemplo, el componente principal puede definir un valor de texto en un componente secundario, o bien invocar un método en este.

Veamos cómo funciona esta comunicación.

Comunicación ascendente de información

Podemos comunicar información hacia arriba mediante eventos y agentes de escucha de eventos.

El componente secundario envía el evento y el principal lo escucha. El envío del evento incluye la creación de un objeto de evento que el componente secundario puede pasar al principal. El componente principal contiene un controlador 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 sencillo 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'));

  }

}    
Nota

Los tipos de evento pueden ser cualquier cadena, pero deben cumplir con el formato de evento DOM estándar de no contener letras en mayúscula ni espacios y usar guiones bajos para separar las palabras en caso de que sea necesario.

El componente principal escucha el evento con el controlador de eventos en línea prefijado con 'on'(onnext).

<!-- todoApp.html -->

<template>

  <c-todo-item onnext={nextHandler}></c-todo-item>

</template>

Después, pasa el objeto de evento al controlador de eventos.

// todoApp.js

import { LightningElement } from 'lwc';

export default class TodoApp extends LightningElement {

  ... 

  nextHandler(){ 

    this.page = this.page + 1;

  }

} 

Comunicación descendente de información

La información se puede comunicar en sentido descendente mediante propiedades públicas y métodos públicos.

Para hacer que una propiedad de componente sea pública, puede agregarle delante el decorador @api. A continuación, defina la propiedad pública con 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;

}

Defina el valor del principal con lo siguiente:

<!-- todoApp.html -->

<template>

  <c-todo-item item-name="Milk"></c-todo-item>

</template>

Observe que la variable itemName se define mediante el atributo con el tipo de convención kebab item-name. Los nombres de propiedades en JavaScript se escriben con una combinación de mayúsculas y minúsculas, mientras que los de atributos HTML se escriben con la convención kebab (separados por guiones) para que coincida con los estándares de HTML. El atributo item-name del marcado se asigna a la propiedad de JavaScript itemName.

Las propiedades públicas son una buena solución para comunicar en sentido descendente valores primitivos, objetos simples y matrices.

Además, puede usar métodos getter y setter para ejecutar lógica al obtener o definir propiedades. Recuerde anotarlas con el decorador @api para hacerlas públicas para otros componentes.

De forma similar, puede crear métodos públicos a los que se pueda llamar desde un componente principal. Cree un método público en el componente secundario. Para ello, defínalo con el decorador @api y luego 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 se incluye el componente c-video-player en un componente principal, podemos invocar el método desde el componente de esta manera:

// 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 activa el evento. Después usamos el método DOM querySelector() para buscar un elemento DOM denominado c-video-player e invocar este método público.

Gestión de eventos en HTML

Nuestra aplicación de selector necesita gestionar un tipo de evento: cuando el usuario hace clic en un icono. Cuando esto ocurra, el componente detail (detalles) debe volver a representarse con la información del icono relacionado. Puede gestionar eventos en HTML (agregando un agente de escucha de eventos en la plantilla) o JavaScript (escribiendo una función de agente de escucha de eventos). Recomendamos usar el enfoque de HTML de la siguiente manera.

Cada componente tile (icono) escucha el clic del usuario, puesto que el archivo HTML del componente (tile.html) contiene un agente 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}></img>

    </a>

  </div>

</template>

Cuando un usuario hace clic en uno de los iconos de la interfaz de usuario, el agente de escucha onclick llama a la función del controlador tileClick del 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 evento en la aplicación Selector

En nuestra aplicación de selector de productos, usamos un componente complejo (uno que contiene varios componentes principales y secundarios). Recomendamos propagar el evento en sentido ascendente por toda la jerarquía de componentes para que los componentes principales puedan responder a los eventos de los secundarios. Si tiene otros componentes secundarios (no el que desencadena el evento), puede pasar una propiedad hacia abajo a esos componentes en respuesta al evento.

El patrón tiene el siguiente aspecto:

Flujo de eventos por la jerarquía de componentes.

Para hacerlo, necesitamos encadenar agentes de escucha de eventos y controladores en la jerarquía hasta llegar al componente ebikes. Después, debemos pasar una propiedad hacia abajo hasta el componente detail.

En nuestros archivos, verá lo siguiente.

  1. tile.html contiene el agente de escucha de eventos onclick que llama al controlador tileClick.
  2. tile.js contiene el método tileClick que crea un nuevo CustomEvent con el tipo de evento tileclick y un objeto que contiene un valor detail (this.product.fields.Id.value).
  3. list.html contiene el agente de escucha ontileclick que llama al controlador handleTileClick.
  4. list.js contiene el método handleTileClick que pasa el evento(evt) para crear otro evento CustomEvent(productselected) con un objeto que también contiene un valor detail evt.detail. Después, se envía el evento en JavaScript:

    // Fire the event from c-list
    this.dispatchEvent(event);

  5. selector.html contiene el agente de escucha de eventos onproductselected que llama al controlador handleProductSelected.
  6. selector.js contiene el método handleProductSelected con selectedProductId definido con el valor evt.detail que pasamos anteriormente. La variable "selectedProductId" se pasa desde el componente selector hasta el componente detail en selector.htm:
    product-id={selectedProductId}.
  7. detail.html contiene una directiva condicional (¿las recuerda de la Unidad 2?) que espera un valor de producto:
    <template lwc:if={product}>
  8. detail.js une todas las partes. Crea una variable privada _productId para rastrear el estado del valor de productId. Después, usa un patrón get/set para obtener el valor y establecerlo en una variable product que permita al archivo detail.html cargar el contenido condicional.

Los elementos getter y setter son una construcción típica de JavaScript. Permiten agregar lógica y condiciones a 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; 

  } 

}

Cada vez que hace clic en un icono, se repite este mismo proceso.

Nota

Los eventos contienen propiedades para gestionar la propagación del evento en sentido ascendente por el árbol de DOM. Encontrará más información en Configurar la propagación de eventos. El cambio de los valores predeterminados es para la gestión de eventos avanzada, y requiere la realización de pruebas para garantizar el comportamiento esperado.

Implementar los archivos en su organización

Vamos a implementar estos nuevos archivos del proyecto bikeCard en su organización para ver cómo funciona todo. Siguiendo los mismos pasos de la última unidad, implemente los archivos nuevos, abra la organización y cree una página en Lightning App Builder con esta aplicación.

  1. En el proyecto bikeCard de VS Code, haga clic con el botón derecho en la carpeta force-app/main/default y seleccione SFDX: Deploy Source to Org (SFDX: Implementar código fuente en la organización).
  2. En Command Palette (Paleta de comandos) en VS Code, use la opción SFDX: Open Default Org (Abrir organización predeterminada) para abrir la organización.
  3. Cree una página de una región con el componente selector (selector).
  4. Asígnele la etiqueta Your Bike Selection (Su selección de bicicleta).
  5. Arrastre el componente selector (selector) a la parte superior del formato de página.
  6. Guarde y active el componente para todos los usuarios.
  7. Ábralo y vea cómo funciona en la interfaz de usuario.

Ahora tiene una página completamente interactiva compuesta por varios componentes que funcionan en conjunto. A continuación, vamos a experimentar con la aplicación de estilo y la obtención de datos en tiempo real de una organización.

Recursos

¡Siga aprendiendo gratis!
Regístrese para obtener una cuenta y continuar.
¿Qué hay para usted?
  • Consiga recomendaciones personalizadas para sus objetivos profesionales
  • Practique sus habilidades con retos prácticos y pruebas
  • Siga y comparta su progreso con empleadores
  • Póngase en contacto para recibir asesoramiento y oportunidades laborales