Skip to main content

Gestire gli eventi nei componenti Web Lightning

Obiettivi di apprendimento

Al completamento di questa unità, sarai in grado di:

  • Creare un'app costituita da più componenti.
  • Descrivere la struttura di file di un componente complesso.
  • Gestire gli eventi.

Seguire il journey di un evento

Hai creato un componente e lo hai reso disponibile in un'organizzazione. Iniziamo ad aggiungere alcune funzionalità interattive con la gestione eventi. Seguiamo il journey di un evento attraverso i vari componenti per la gestione articolata dell'evento in un'applicazione. L'applicazione è un selettore di prodotti per un negozio di biciclette. Gli utenti fanno clic sul nome e sull'immagine di una bicicletta per visualizzarne i dettagli.

Le parti che compongono il selettore di biciclette.

In questa app abbiamo quattro componenti che interagiscono tra loro.

  1. tile (riquadro): mostra il singolo articolo.
  2. list (elenco): dispone i riquadri.
  3. detail (dettagli): mostra i dettagli dell'articolo quando si fa clic su un riquadro (in modo analogo al componente bikeCard che hai appena creato).
  4. selector (selettore): contiene l'intero set di componenti. Non è necessario un componente container (contenitore), tuttavia in questo caso ne useremo uno per facilitare la gestione degli eventi.

Per il momento, l'applicazione utilizza un file di dati per caricare dati statici a scopo di testing. Nella prossima unità imparerai a estrarre dati dinamici da un'organizzazione.

Composizione del componente

Aggiungiamo al progetto alcuni file che possiamo distribuire in un'organizzazione.

  1. Scaricai file dell'app da qui: Bike Selector App for Trailhead (App Selettore di bici per Trailhead).
  2. Decomprimi i file nella cartella force-app/main/default/lwc del progetto bikeCard. Struttura dei file dell'app Bike Selector.

Relazioni dei componenti

In quest'app più componenti lavorano assieme; alcuni componenti sono nidificati all'interno di altri. Analogamente a quanto accade per gli elementi HTML, è possibile nidificare i componenti Web Lightning, che sono di fatto elementi HTML personalizzati, all'interno di altri componenti Web Lightning.

Nel file system non è possibile intuire dalle cartelle dei componenti quale sia la relazione tra di essi.

Usiamo un diagramma per vedere come sono nidificati i componenti a livello di interfaccia utente.

Relazioni principale/secondario tra i componenti dell'app Bike Selector.

Osservando i file puoi vedere che il componente selector determina il layout della pagina e visualizza i componenti list (c-list) e 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>

Aggiorna detail.html con il codice seguente:

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

Se osservi detail.html, vedrai che il rendering è condizionale (lwc:if={product} e lwc:else). Se l'utente non ha selezionato nulla dall'elenco, un messaggio chiede all'utente di selezionare un articolo. Se è stata effettuata una selezione, vengono visualizzate le informazioni relative alla bici.

Il componente list esegue il rendering di più componenti tile (c-tile), uno per ogni bici presente nei dati. Questa nidificazione viene realizzata nel codice HTML per ogni componente principale. Ad esempio, il codice HTML del componente list riportato di seguito include il componente tile come elemento 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>

Come vedi, ciascuna iterazione dell'articolo bici genera un nuovo componente tile. È sufficiente includere il tag del componente c-tile per far sì che ogni componente tile diventi un suo componente secondario. Puoi controllare la disposizione dei riquadri grazie alla definizione div class "container" utilizzata nello stile. Se guardi nel file list.css, vedrai che racchiude il contenuto.

.container { display:flex; flex-direction:row; flex-wrap:wrap; }

La relazione principale/secondario è importante non solo per la progettazione dell'app, ma anche per la gestione degli eventi.

Analizziamo più a fondo la gestione degli eventi.

Eventi verso l'alto, proprietà verso il basso

In un componente complesso (che contiene diversi componenti principali e secondari) i componenti possono comunicare verso l'alto e verso il basso.

Componenti principali e secondari che passano informazioni verso l'alto o verso il basso.

  1. Il componente secondario c-todo-item invia un evento al componente principale c-todo-app. Ad esempio, quando l'utente fa clic su un pulsante, il componente secondario può passare un oggetto evento a quello principale per consentirgli di gestire l'evento e cambiare la pagina corrente.
  2. Il componente principale c-todo-app passa una proprietà o chiama un metodo del componente secondario. Ad esempio, il componente principale può impostare un valore di testo in un componente secondario o chiamare un metodo di quel componente.

Vediamo come funziona la comunicazione.

Passare le informazioni al livello superiore

Le informazioni possono essere passate al livello superiore utilizzando eventi e listener di eventi.

Il componente secondario invia l'evento mentre il componente principale è in ascolto. L'invio dell'evento comporta la creazione di un oggetto evento che il componente secondario può passare a quello principale, che contiene un gestore per rispondere all'evento.

Ad esempio (non creare questi componenti) un componente secondario come quello seguente contiene un metodo nextHandler() che crea un oggetto evento semplice utilizzando CustomEvent() e invia il tipo di evento "next" quando l'utente fa clic su un pulsante Next (Avanti).

// todoItem.js

import { LightningElement } from 'lwc';

  ...

  nextHandler() {

    this.dispatchEvent(new CustomEvent('next'));

  }

}    
Nota

I tipi di evento possono essere qualsiasi stringa, ma devono essere conformi allo standard degli eventi DOM, ovvero: nessuna lettera maiuscola, nessuno spazio e, se necessario, trattino basso per separare le parole.

Il componente principale è in ascolto per intercettare l'evento con il gestore di eventi inline preceduto dal prefisso "on"(onnext).

<!-- todoApp.html -->

<template>

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

</template>

Quindi passa l'oggetto evento a un gestore di eventi.

// todoApp.js

import { LightningElement } from 'lwc';

export default class TodoApp extends LightningElement {

  ... 

  nextHandler(){ 

    this.page = this.page + 1;

  }

} 

Passare le informazioni al livello inferiore

Le informazioni possono essere passate al livello inferiore utilizzando proprietà pubbliche e metodi pubblici.

Per rendere pubblica la proprietà del componente falla precedere dal decorator @api. Quindi, imposta la proprietà pubblica a partire da un componente esterno.

Ad esempio (non creare questi componenti), se il componente secondario c-todo-item contiene il codice seguente:

// todoItem.js

import { LightningElement, api } from 'lwc';

export default class TodoItem extends LightningElement {

  @api itemName;

}

Imposta il valore a partire dal componente principale con il codice seguente:

<!-- todoApp.html -->

<template>

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

</template>

Nota che la variabile itemName viene impostata usando l'attributo item-name che utilizza la notazione kebab-case. I nomi delle proprietà in JavaScript usano la notazione camelCase, mentre i nomi degli attributi usano quella kebab-case (separazione con trattino) per rispettare gli standard HTML. L'attributo item-name (nome-articolo) del markup viene mappato alla proprietà JavaScript itemName.

Le proprietà pubbliche sono un'ottima soluzione per passare valori primitivi, oggetti semplici e array.

Inoltre, per eseguire logica quando le proprietà vengono lette o impostate, puoi usare metodi getter e setter. E ricorda di annotarle con il decorator @api per renderle pubbliche per gli altri componenti.

Analogamente, puoi creare metodi pubblici che possono essere chiamati da un componente principale. Puoi creare un metodo pubblico nel componente secondario definendolo con il decorator @api, quindi chiamarlo dal componente principale.

Poniamo di avere un componente secondario come il seguente (non creare questi componenti).

// videoPlayer.js

import { LightningElement, api } from 'lwc';

export default class VideoPlayer extends LightningElement {

  @api play() {

    // Play music!

  }

}

Quando il componente c-video-player è incluso in un componente principale, possiamo chiamare il metodo dal componente principale in questo modo:

// methodCaller.js

import { LightningElement } from 'lwc';

export default class MethodCaller extends LightningElement {

  handlePlay() {

    this.template.querySelector('c-video-player').play();

  }

}

Abbiamo definito un metodo handlePlay() che innesca l'evento. Quindi, usiamo il metodo DOM querySelector() per cercare un elemento DOM denominato c-video-player e chiamarne il metodo pubblico.

Gestire gli eventi in HTML

La nostra app selector deve gestire un solo tipo di evento: l'utente che fa clic su un riquadro. Quando questo accade, il componente detail deve rieseguire il rendering delle informazioni dal riquadro correlato. Puoi gestire gli eventi in HTML (aggiungendo un listener di eventi al modello) o in JavaScript (scrivendo una funzione listener di eventi). È consigliabile utilizzare il codice HTML, come di seguito.

Ogni componente tile è in ascolto per intercettare il clic dell'utente, in quanto il codice HTML del componente tile (tile.html) contiene un listener di eventi 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>

Quando un utente fa clic su una delle istanze del componente tile nell'interfaccia utente, il listener onclick chiama la funzione gestore tileClick nel file 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);

  }

} 

Sequenza eventi dell'app selector

Nella nostra app per la selezione dei prodotti utilizziamo un componente complesso (che contiene diversi componenti principali e secondari). Ti consigliamo di propagare l'evento verso l'alto tramite la gerarchia dei componenti, affinché i componenti principali possano rispondere agli eventi innescati nei componenti secondari. Se hai altri componenti secondari (diversi da quello che innesca l'evento) puoi passare una proprietà al livello inferiore a quei componenti secondari in risposta all'evento.

La sequenza è illustrata di seguito:

L'evento attraversa la gerarchia componenti.

Per farlo dobbiamo concatenare i listener e i gestori di eventi risalendo la gerarchia fino al componente ebikes, quindi passare una proprietà verso il basso al componente detail.

Ecco cosa vedi nei nostri file:

  1. tile.html contiene il listener di eventi onclick che chiama il gestore tileClick.
  2. tile.js contiene il metodo tileClick che crea un nuovo CustomEvent con il tipo di evento tileclick e un oggetto che contiene un valore detail (this.product.fields.Id.value).
  3. list.html contiene il listener ontileclick che chiama gestore handleTileClick.
  4. list.js contiene il metodo handleTileClick che passa l'evento (evt) per creare un altro CustomEvent (productselected) con un oggetto che contiene anch'esso un valore detail, evt.detail. Quindi, invia l'evento in JavaScript:

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

  5. selector.html contiene il listener di eventi onproductselected che chiama il gestore handleProductSelected.
  6. selector.js contiene il metodo handleProductSelected che imposta selectedProductId sul valore evt.detail che gli era stato passato. La variabile "selectedProductId" viene passata dal componente selector al componente detail in selector.html:
    product-id={selectedProductId}.
  7. detail.html contiene una direttiva condizionale (hai presente quelle citate nell'unità 2?) che prevede un prodotto come valore:
    <template lwc:if={product}>
  8. detail.js mette insieme tutti i pezzi. Crea una variabile privata _productId per tenere traccia dello stato del valore productId. Quindi utilizza uno schema get/set per leggere il valore e impostarlo su una variabile product che consente a detail.html di caricare il contenuto condizionale.

I metodi getter e setter sono tipici di JavaScript. Consentono di aggiungere logica e condizioni alle assegnazioni delle proprietà.

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; 

  } 

}

Ogni volta che fai clic su un riquadro il processo si ripete.

Nota

Gli eventi hanno proprietà per gestire la propagazione dell'evento fino all'albero DOM. Per maggiori informazioni puoi consultare Configure Event Propagation (Configurare la propagazione degli eventi). La modifica dei valori predefiniti è un'attività di gestione degli eventi avanzata e deve essere testata per assicurare che il funzionamento sia quello previsto.

Distribuire i file nell'organizzazione

Distribuiamo questi nuovi file del progetto bikeCard nell'organizzazione per vedere come funzionano. Segui la stessa procedura usata nell'unità precedente per distribuire i nuovi file: apri l'organizzazione e crea una pagina in Lightning App Builder (Generatore di app Lightning) con quest'app.

  1. Nel progetto bikeCard di VS Code, fai clic con il tasto destro sulla cartella force-app/main/default e seleziona SFDX: Deploy Source to Org (SFDX: distribuisci sorgente a Org).
  2. Dal riquadro dei comandi in VS Code, usa SFDX: Open Default Org (SFDX: apri org predefinita) per aprire l'organizzazione.
  3. Crea una pagina con una sola regione con il componente selector.
  4. Assegna l'etichetta Your Bike Selection (Selezione bicicletta).
  5. Trascina il componente selector in cima al layout di pagina.
  6. Salva e attiva per tutti gli utenti.
  7. Apri la pagina e guarda come funziona il componente nell'interfaccia utente.

Ora hai una pagina completamente interattiva costituita da vari componenti che interagiscono tra loro. Tra breve faremo qualche esperimento con lo stile e sulla ricezione di dati in tempo reale da un'organizzazione.

Risorse

Continua a imparare gratuitamente!
Registra un account per continuare.
Cosa troverai?
  • Ottieni consigli personalizzati per i tuoi obiettivi di carriera
  • Metti in pratica le tue competenze con sfide pratiche e quiz
  • Monitora e condividi i tuoi progressi con i datori di lavoro
  • Accedi a risorse di tutoraggio e opportunità di carriera