Skip to main content
Build the future with Agentforce at TDX in San Francisco or on Salesforce+ on March 5–6. Register now.

Tratar de eventos em Componentes Web do Lightning

Objetivos de aprendizagem

Após concluir esta unidade, você estará apto a:

  • Criar um aplicativo que inclua vários componentes.
  • Descrever a estrutura de arquivos de um componente complexo.
  • Tratar de eventos.

Seguir a jornada de um evento

Você criou um componente e enviou-o a uma organização. Vamos começar adicionando interatividade com o tratamento de eventos. Seguimos a jornada de um evento por vários componentes para ver o tratamento de eventos sofisticados em um aplicativo. Este aplicativo é um seletor de produtos de uma loja de bicicletas. Os usuários clicam no nome e na imagem de uma bicicleta para ver mais detalhes.

As partes do componente do seletor de bicicletas.

Este aplicativo tem quatro componentes trabalhando em conjunto.

  1. tile: exibe um item individual.
  2. list: organiza os blocos.
  3. detail: exibe detalhes do item quando um bloco é clicado (semelhante ao bikeCard que você acabou de criar).
  4. selector: contém todo o conjunto de componentes. Não é obrigatório um contêiner de componente, mas estamos usando um aqui para ajudar no tratamento de eventos.

Por enquanto, o aplicativo usa um arquivo de dados para carregar dados estáticos para testes. Na próxima unidade, você aprenderá a extrair dados dinâmicos de uma organização.

Composição de componentes

Vamos adicionar ao projeto alguns arquivos que podem ser implantados em uma organização.

  1. Baixe os arquivos para este aplicativo aqui: Aplicativo seletor de bicicletas do Trailhead.
  2. Descompacte os arquivos na pasta force-app/main/default/lwc do projeto bikeCard. Estrutura de arquivos do aplicativo seletor de bicicletas.

Relacionamentos do componente

Neste aplicativo, vários componentes funcionam em conjunto; alguns componentes estão aninhados dentro de outros componentes. Da mesma forma que você aninha elementos HTML dentro uns dos outros, os componentes Web do Lightning, que são elementos HTML personalizados, podem ser aninhados dentro de outros componentes Web do Lightning.

Em nosso sistema de arquivos, as pastas dos componentes realmente não dão indícios sobre os relacionamentos entre eles.

Vamos ver como os componentes foram aninhados no nível da interface do usuário em um diagrama.

Os relacionamentos pai/filho do componente do aplicativo seletor de bicicletas.

Ao olhar para os arquivos, você pode ver que o componente selector define a página e torna os componentes da lista (c-list) e detalhes (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>

Atualize detail.html com o seguinte:

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

Olhando detail.html, você pode ver renderização condicional (lwc:if={product} e lwc:else). Se não tiver nada escolhido na lista, será exibida uma mensagem solicitando que o usuário escolha algo. Se algo for escolhido, serão exibidas informações sobre a bicicleta.

O componente list processa vários componentes tile(c-tile), um para cada bicicleta nos dados. Esse aninhamento é alcançado no HTML para cada componente pai. Por exemplo, o componente list tem o seguinte HTML, incluindo o componente tile 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 como cada iteração do item da bicicleta gera um novo componente tile. A simples inclusão da marca do componente c-tile torna todos os componentes tile em filho. A definição da div class "container" é usada para adicionar estilos para que você possa controlar a organização dos blocos. Se observar list.css, verá que ela encapsula o conteúdo.

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

O relacionamento pai/filho é importante, não só para o design do aplicativo, mas, também, para o tratamento de eventos.

Vamos nos aprofundar um pouco mais no tratamento de eventos.

Eventos para cima, propriedades para baixo

Em um componente complexo (um que contenha vários componentes pai e filho), os componentes podem se comunicar para cima e para baixo.

Componentes pai e filho transmitindo informações para cima e para baixo.

  1. O componente filho c-todo-item envia um evento ao componente pai c-todo-app. Por exemplo, o componente filho pode transmitir um objeto de evento para o componente pai quando um usuário clica em um botão para que o pai possa tratar do evento e alterar a página atual.
  2. O componente pai c-todo-app transmite uma propriedade ou invoca um método no componente filho. Por exemplo, o pai pode definir um valor de texto em um componente filho ou invocar um método no componente filho.

Vamos ver como essa comunicação funciona.

Como transmitir informações para cima

As informações podem ser transmitidas usando eventos e ouvintes de eventos.

O componente filho envia o evento e o componente pai escuta-o. O envio do evento inclui a criação de um objeto de evento que o filho pode transmitir ao componente pai. O pai tem um manipulador para responder ao evento.

Por exemplo (não crie esses componentes), um componente filho como este contém um método nextHandler() que cria um objeto de evento simples usando CustomEvent() e envia o tipo de evento 'next' quando o usuário clica em um botão Avançar.

// todoItem.js
import { LightningElement } from 'lwc';
  ...
  nextHandler() {
    this.dispatchEvent(new CustomEvent('next'));
  }
}
Nota

Os tipos de eventos podem ser qualquer sequência, mas devem estar em conformidade com o padrão de evento DOM: sem letras maiúsculas, sem espaços e usar sublinhados para separar palavras, se necessário.

O componente pai ouve o evento com o manipulador de eventos inline prefixado com 'on'(onnex).

<!-- todoApp.html -->
<template>
  <c-todo-item onnext={nextHandler}></c-todo-item>
</template>

E transmite o objeto do evento a um manipulador de eventos.

// todoApp.js
import { LightningElement } from 'lwc';
export default class TodoApp extends LightningElement {
  ...
  nextHandler(){
    this.page = this.page + 1;
  }
}

Como transmitir informações para baixo

As informações podem ser transmitidas usando propriedades públicas e métodos públicos.

Torne uma propriedade de componente pública precedendo-a com o decorador @api. Em seguida, defina a propriedade como pública por meio de um componente externo.

Por exemplo (não crie esses componentes), se o componente filho c-todo-item tiver o seguinte:

// todoItem.js
import { LightningElement, api } from 'lwc';
export default class TodoItem extends LightningElement {
  @api itemName;
}

Defina o valor do pai com o seguinte:

<!-- todoApp.html -->
<template>
  <c-todo-item item-name="Milk"></c-todo-item>
</template>

Observe que a variável itemName é definida usando o atributo com palavras separadas por hifens item-name. Os nomes de propriedade no JavaScript usam letras concatenadas, enquanto os nomes de atributos HTML usam palavras separadas por hifens para corresponder aos padrões HTML. O atributo item-name na marcação mapeia para a propriedade itemName do JavaScript.

As propriedades públicas são ótimas soluções para transmitir valores primitivos, objetos simples e matrizes.

Além disso, você pode utilizar getters e setters para usar lógica quando as propriedades são get ou set. Lembre-se de anotá-los com o decorador @api para torná-los públicos para outros componentes.

Da mesma forma, você pode criar métodos públicos que podem ser chamados a partir de um componente pai. Crie um método público no componente filho definindo-o com o decorador @api e, em seguida, chame-o do componente pai.

Vamos supor que temos um componente filho como este (não crie esses componentes).

// videoPlayer.js
import { LightningElement, api } from 'lwc';
export default class VideoPlayer extends LightningElement {
  @api play() {
    // Play music!
  }
}

Quando o componente c-video-player é incluído em um componente pai, podemos invocar o método do componente pai da seguinte maneira:

// methodCaller.js
import { LightningElement } from 'lwc';
export default class MethodCaller extends LightningElement {
  handlePlay() {
    this.template.querySelector('c-video-player').play();
  }
}

Definimos um método handlePlay() que aciona o evento. Em seguida, usamos o método DOM querySelector() para pesquisar um elemento DOM chamado c-video-player e invocar seu método público.

Tratamento de eventos em HTML

Nosso aplicativo seletor precisa tratar de um tipo de evento: o usuário que clica em um bloco. Quando isso acontece, o componente detail deve voltar a realizar a renderização com as informações do bloco relacionado. Você pode tratar de eventos em HTML (adicionar um ouvinte para o evento no modelo) ou JavaScript (escrever uma função de ouvinte para o evento). Recomendamos o uso da abordagem HTML da seguinte maneira.

Cada componente tile ouve o clique do usuário porque o HTML do componente tile (tile.html) tem um ouvinte para o evento 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>

Quando um usuário clica em uma das instâncias de blocos na interface do usuário, o ouvinte onclick chama a função tileClick do manipulador no arquivo 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);
  }
}

Padrão de eventos do aplicativo seletor

Em nosso aplicativo seletor de produtos, usamos um componente complexo (que contém vários componentes pai e filho). Recomendamos que você propague o evento por toda hierarquia de componentes para que os componentes pai possam responder a eventos filho. Se você tiver outros componentes filho (não o que aciona o evento), como resposta ao evento, poderá transmitir uma propriedade para baixo, a esses filhos.

O padrão tem essa aparência:

Fluxo de eventos pela hierarquia de componentes.

Para fazer isso, precisamos encadear os ouvintes e manipuladores de eventos para cima, pela hierarquia, para o componente ebikes. Em seguida, precisamos transmitir uma propriedade para baixo ao componente detail.

Em nossos arquivos, você verá o seguinte.

  1. tile.html tem o ouvinte de evento onclick que chama o manipulador tileClick.
  2. tile.js tem o método tileClick que cria um novo CustomEvent com o tipo de evento tileclick e um objeto contendo um valor detail (this.product.fields.Id.value).
  3. list.html tem o ouvinte ontileclick que chama o manipulador handleTileClick.
  4. list.js tem o método handleTileClick que passa no evento (evt) para criar outro CustomEvent(productselected) com um objeto também contendo um valor detail evt.detail. E envia o evento em JavaScript:
    // Fire the event from c-list
    this.dispatchEvent(event);

  5. selector.html tem o ouvinte de evento onproductselected que chama o manipulador handleProductSelected.
  6. selector.js tem o método handleProductSelected definido como selectedProductId para o valor evt.detail que foi passado para ele. A variável "selectedProductId" é passada do componente selector para o componente detail em selector.html:
    product-id={selectedProductId}.
  7. detail.html tem uma diretiva condicional (você se lembra das diretivas da unidade 2?) que aguarda um valor product:
    <template lwc:if={product}>
  8. detail.js reúne as partes. Ele cria uma variável privada _productId para rastrear o estado do valor productId. Em seguida, ele usa um padrão get/set para obter o valor e configurá-lo para uma variável product que permite que detail.html carregue o conteúdo condicional.

Getters e setters são uma construção comum de JavaScript. Eles permitem adicionar lógica e condições a atribuições de propriedade.

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 você clica em um bloco, esse processo se repete.

Nota

Os eventos têm propriedades para gerenciar a propagação do evento para cima, pela árvore DOM. Leia mais sobre eles em Configurar a propagação de eventos. A alteração dos valores padrão destina-se ao tratamento avançado de eventos e exige testes para garantir o comportamento esperado.

Implantar seus arquivos na organização

Vamos implantar esses novos arquivos do projeto bikeCard em sua organização para ver como ela funciona. Usando as mesmas etapas executadas na última unidade, implante os novos arquivos, abra a organização e crie uma página no Criador de aplicativo Lightning com esse aplicativo.

  1. No projeto bikeCard do VS Code, clique com o botão direito do mouse na pasta force-app/main/default e selecione SFDX: Deploy Source to Org (SFDX: Implantar origem na organização).
  2. Na Paleta de comando no VS Code, use SFDX: Open Default Org (Abrir a organização padrão) para abrir sua organização.
  3. Crie a página de uma região usando o componente selector.
  4. Dê-lhe o rótulo Your Bike Selection (Sua seleção de bicicletas).
  5. Arraste seu componente selector para o topo do layout de páginas.
  6. Salve e ative para todos os usuários.
  7. Abra-o e veja o funcionamento do componente na interface do usuário.

Você tem uma página totalmente interativa composta por vários componentes que funcionam em conjunto. Em seguida, vamos experimentar o estilo e a obtenção de dados em tempo real de uma organização.

Recursos

Compartilhe seu feedback do Trailhead usando a Ajuda do Salesforce.

Queremos saber sobre sua experiência com o Trailhead. Agora você pode acessar o novo formulário de feedback, a qualquer momento, no site Ajuda do Salesforce.

Saiba mais Continue compartilhando feedback