Skip to main content

在 Lightning Web 组件中处理事件

学习目标

完成本单元后,您将能够:

  • 创建包含多个组件的应用程序。
  • 描述复杂组件的文件结构。
  • 处理事件。

跟踪事件过程

您已经构建了一个组件并将其推送到组织中。让我们开始为事件处理添加一些交互性。我们通过若干组件跟踪事件的过程,以便在应用程序中处理复杂事件。此应用程序是自行车商店采用的产品选择器。用户通过点击自行车名称和图像来查看更多详细信息。

自行车选择器组件的各部分。

此应用程序有四个协同工作的组件。

  1. tile(磁贴):显示单个项目。
  2. list(列表):排列各磁贴。
  3. detail(详细信息):点击一个磁贴时,会显示该磁贴的详细信息(与您刚创建的 bikeCard 相似)。
  4. selector(选择器):包含整套组件。容器组件并非必需的,但我们在此处使用了一个容器组件,以帮助处理事件。

目前,该应用程序使用数据文件加载静态数据进行测试。在下一单元中,您将学习如何从组织中提取动态数据。

组件构成

让我们在项目中添加一些可以部署到组织的文件。

  1. 在此处下载此应用程序的文件:Trailhead 的自行车选择器应用程序
  2. 将文件解压到 bikeCard 项目的 force-app/main/default/lwc 文件夹中。自行车选择器应用程序文件结构。

组件关系

在此应用程序中,多个组件协同工作;某些组件嵌套在其他组件中。就像 HTML 元素相互嵌套一样,Lightning web 组件(即自定义 HTML 元素)也可以嵌套在其他 Lightning web 组件中。

在我们的文件系统中,组件的文件夹并不能真正深入了解它们之间的关系。

让我们来看看这些组件是如何在 UI 层面上以图表的形式嵌套的。

自行车选择器应用程序组件的父/子级关系。

通过查看文件,您可以看到选择器组件布局了页面,并呈现了列表 (c-list) 和详细信息 (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>

用以下内容更新 detail.html:

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

在 detail.html 中,您可以看到条件渲染(lwc:if={product}lwc:else)。如果未从列表中选择任何内容,则会显示一条要求用户进行选择的消息。如果选择了某个内容,则会显示自行车信息。

列表组件呈现了多个磁贴 (c-tile) 组件,数据中的每款自行车都对应一个组件。这种嵌套是在每个父级组件的 HTML 中实现的。例如,列表组件具有以下 HTML,包括磁贴组件 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>

请注意,自行车项目的每次迭代都会生成一个新的磁贴组件。只要包含 c-tile 组件标签,每个磁贴组件就会成为它的子级组件。Div 类定义“container”(容器)用于设置样式,因此您可以控制磁贴的排列。如果查看 list.css,便能看到它封装了内容。

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

父级/子级关系对于应用程序的设计和事件处理都很重要。

让我们更深入地探讨一下事件处理。

事件向上,属性向下

在复杂组件(包含若干父级和子级组件)中,组件可以向上和向下交流。

父级和子级组件向上和向下传递信息。

  1. 子级组件 c-todo-item 向父级组件 c-todo-app 派发一个事件。例如,当用户点击按钮时,子级组件可以将事件对象传递给父级组件,这样父级组件就可以处理该事件并更改当前页面。
  2. c-todo-app 父级组件在子级组件中传递属性或调用方法。例如,父级组件可以在子级组件中设置文本值,或调用某个方法。

下面让我们来看看这种交流是如何进行的。

向上传递信息

可以使用 events(事件)和 event listeners(事件监听器)向上传递信息。

子级组件派发事件,父级组件监听事件。派发事件包括创建子级组件可以传递给父级组件的事件对象。父级组件有一个处理程序来响应该事件。

例如(不要创建这些组件),像这样的子级组件包含 nextHandler() 方法,该方法使用 CustomEvent() 创建一个简单的事件对象,并在用户点击 Next(下一步)按钮时派发事件类型 'next'。

// todoItem.js

import { LightningElement } from 'lwc';

  ...

  nextHandler() {

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

  }

}    
备注

事件类型可以是任何字符串,但应符合 DOM 事件标准,即不含大写字母,不含空格,必要时用下划线分隔单词。

父级组件用内联事件处理程序监听带有前缀 'on'(onnext) 的事件。

<!-- todoApp.html -->

<template>

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

</template>

并将事件对象传递给事件处理程序。

// todoApp.js

import { LightningElement } from 'lwc';

export default class TodoApp extends LightningElement {

  ... 

  nextHandler(){ 

    this.page = this.page + 1;

  }

} 

向下传递信息

可以使用 public properties(公共属性)和public methods(公共方法)向下传递信息。

您可以使用 @api 装饰器将组件属性变为公共属性。然后通过外部组件来设置公共属性。

例如(不要创建这些组件),c-todo-item 子级组件具有以下内容:

// todoItem.js

import { LightningElement, api } from 'lwc';

export default class TodoItem extends LightningElement {

  @api itemName;

}

使用以下内容设置父级组件的值:

<!-- todoApp.html -->

<template>

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

</template>

请注意,itemName 变量是使用串式命名法属性 item-name 来设置的。JavaScript 中的属性名称采用驼峰式大小写命名法,而 HTML 属性名称则为符合 HTML 标准的串式(用破折号分隔)。标记中的 item-name 属性映射到 itemName JavaScript 属性。

公共属性是传递原始值、简单对象和数组的绝佳解决方案。

此外,当获取或设置属性时,您可以使用 getters 和 setters 来执行一些逻辑。记住要用 @api 装饰器对它们进行注释,使它们对其他组件公开。

类似地,您可以创建可从父级组件调用的公共方法。通过使用 @api 装饰器进行定义以在子级组件中创建一个公共方法,然后从父级组件中调用它。

假设我们有一个像这样的子级组件(不要创建这些组件)。

// videoPlayer.js

import { LightningElement, api } from 'lwc';

export default class VideoPlayer extends LightningElement {

  @api play() {

    // Play music!

  }

}

当 c-video-player 组件被包含在父级组件中时,我们就可以像这样从父级组件中调用该方法:

// methodCaller.js

import { LightningElement } from 'lwc';

export default class MethodCaller extends LightningElement {

  handlePlay() {

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

  }

}

我们定义了一个方法 handlePlay() 来触发该事件。然后使用 querySelector() DOM 方法搜索名为 c-video-player 的 DOM 元素,并调用其公共方法。

在 HTML 中处理事件

因此,我们的选择器应用程序需要处理一种类型的事件——用户点击一个磁贴。当这种情况发生时,详细信息组件应该用相关磁贴的信息重新渲染。您可以用 HTML(在模板中添加事件监听器)或 JavaScript(编写事件监听器函数)处理事件。我们推荐使用 HTML 方法,如下所示。

每个磁贴组件都会监听用户的点击情况,因为磁贴组件的 HTML (tile.html) 包含一个 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>

当用户点击 UI 中的一个磁贴实例时,onclick 监听器会调用 tile.js JavaScript 文件中的处理函数 tileClick

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

  }

} 

选择器应用程序的事件模式

在产品选择器应用程序中,我们使用了一个复杂的组件(包含若干父级组件和子级组件)。我们建议您通过组件的层次结构向上传播事件,以便父级组件可以响应子级事件。如果您有其他子级组件(并非触发事件的那一个),则可以将属性向下传递给这些子级组件以响应事件。

模式如下所示:

事件通过组件层次结构流动。

为此,我们需要将事件监听器和处理程序在层次结构中向上链接到 ebikes 组件。然后向下传递一个属性给详细信息组件。

在我们的文件中,您可以看到以下内容。

  1. tile.html 具有调用 tileClick 处理程序的 onclick 事件监听器。
  2. Tile.js 具有 tileClick 方法,可以创建事件类型为 tileclick 的新 CustomEvent 和包含 detail 值 (this.product.fields.Id.value) 的对象。
  3. list.html 具有调用 handleTileClick 处理程序的 ontileclick 监听器。
  4. List.js 具有 handleTileClick 方法,它传入事件 (evt) 来创建另一个 CustomEvent(productselected),带有同样包含 detailevt.detail 的对象。同时在 JavaScript 中派发该事件:

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

  5. selector.html 具有调用 handleProductSelected 处理程序的 onproductselected 事件监听器。
  6. Selector.js 具有 handleProductSelected 方法,它将 selectedProductId 设置为传入的 evt.detail 值。变量 "selectedProductId" 从选择器组件传递到 selector.htm 中的详细信息组件:
    product-id={selectedProductId}
  7. detail.html 有一个条件指令(还记得第 2 单元中的指令吗?)等待着产品值:
    <template lwc:if={product}>
  8. detail.js 将这些部分结合起来。它会创建一个私有变量 _productId 来跟踪 productId 值的状态。然后使用获取/设置模式来获取该值,并将其设置为变量 product(产品),让 detail.html 加载条件内容。

Getter 和 setter 是常见的 JavaScript 结构。它们使您能够为属性分配添加逻辑和条件。

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; 

  } 

}

每次点击磁贴时,该过程都会重复。

备注

事件具有可管理事件在 DOM 树上向上传播的属性。您可以在配置事件传播中阅读更多相关信息。更改默认值是用于高级事件处理的,并且需要进行测试以确保预期的行为。

将文件部署到组织

让我们将这些新的 bikeCard 项目文件部署到您的组织中,看看它是如何工作的。部署新文件的步骤与上一单元相同,打开组织,在 Lightning 应用程序生成器中用这个应用程序创建一个页面。

  1. 在 VS Code bikeCard 项目中,右击 force-app/main/default 文件夹,并选择 SFDX: Deploy Source to Org(SFDX:将源部署到组织)
  2. 在 VS Code 命令面板中,使用 SFDX: Open Default Org(SFDX:打开默认组织),打开您的组织。
  3. 使用选择器组件创建一个区域页面。
  4. 添加一个标签 Your Bike Selection(您选择的自行车)。
  5. selector(选择器)组件拖动到页面布局的顶部。
  6. 保存并为所有用户激活。
  7. 打开后可以看到组件已经出现在 UI 中。

至此,您就拥有了一个完全交互式的页面,由若干协同工作的组件组成。接下来,我们将尝试设置样式,并从组织获取实时数据。

资源

继续免费学习!
注册帐户以继续。
有什么适合您的内容?
  • 为您的职业目标获取个性化推荐
  • 通过实践挑战和测验练习您的技能
  • 跟踪并与雇主分享您的进度
  • 与人联系以获取指导和就业机会