進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

Lightning Web コンポーネントでのイベントの処理

学習の目的

この単元を完了すると、次のことができるようになります。
  • 複数のコンポーネントを含むアプリケーションを作成する。
  • 複雑なコンポーネントのファイル構造を説明する。
  • イベントを処理する。

イベントのジャーニー

コンポーネントを作成してスクラッチ組織にプッシュしました。ここでは、イベント処理とのインタラクションを追加しましょう。アプリケーションで高度なイベント処理を行うために複数のコンポーネントを進むイベントのジャーニーを確認します。このアプリケーションは、サイクルショップの商品セレクタです。ユーザは自転車名と画像をクリックして詳細を表示します。

自転車セレクタのコンポーネントパーツ。

このアプリケーションでは 4 つのコンポーネントが連携しています。

  1. tile: 個々の品目を表示します。
  2. list: タイルを配置します。
  3. detail: タイルがクリックされると品目の詳細を表示します (作成した bikeCard に類似)。
  4. selector: すべてのコンポーネントが含まれます。コンテナコンポーネントは必要ありませんが、ここでは、イベント処理をしやすくするためにコンテナを 1 つ使用します。

当面、アプリケーションはデータファイルを使用してテスト用に静的データを読み込みます。次の単元では、組織から動的データを取得する方法を学習します。

コンポーネントのコンポジション

では、いくつかのファイルをプロジェクトに追加して組織にリリースできるようにしましょう。

  1. このアプリケーションのファイルを Trailhead 用自転車セレクタアプリケーションからダウンロードします。
  2. ファイルを E-Bikes デモのプロジェクトの lwc ディレクトリに解凍します。 自転車セレクタアプリケーションのファイル構造。

コンポーネントのリレーション

このアプリケーションでは、複数のコンポーネントが連携します。他のコンポーネント内にネストされているコンポーネントもあります。HTML 要素が互いにネストできるように、単なるカスタム HTML 要素である Lightning Web コンポーネントは、他の Lightning Web コンポーネント内にネストできます。

ファイルシステムでは、コンポーネントのフォルダからコンポーネント間のリレーションに関するインサイトは得られません。

UI レベルでコンポーネントがどうネストされるか図で見てみましょう。 自転車セレクタアプリケーションコンポーネントの親/子リレーション。

selector コンポーネントはページを配置し、list および detail コンポーネントを表示します。list コンポーネントは、データの自転車ごとに 1 つずつ、複数の tile コンポーネントを表示します。

<template>
    <div class="wrapper">
    <header class="header">Available Bikes</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 if:true={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 if:false={product}>
        <div>Select a bike</div>
    </template>
</template>

このネストは、各親コンポーネントの HTML で行われます。たとえば、list コンポーネントには、tile コンポーネント (c-tile) を含む、次の HTML があります。

<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>
自転車品目の反復ごとに新しい tile コンポーネントが発生する方法に注目してください。c-tile コンポーネントタグを含めるだけで、各 tile コンポーネントが子になります。div クラス定義の "container" がスタイルに使用されているため、タイルの配置を制御できます。list.css を見ると、コンテンツがラップされていることがわかります。
.container {
   display: flex;
   flex-direction: row;
   flex-wrap: wrap;
}

この親/子リレーションは、アプリケーションの設計上だけでなく、イベント処理にとっても重要です。

では、イベント処理についてもう少し掘り下げましょう。

HTML でのイベント処理

このアプリケーションは、ある種別のイベント、つまりユーザによるタイルのクリックを処理する必要があります。このイベントが発生すると、detail コンポーネントを関連タイルからの情報と共に再表示する必要があります。イベントは HTML (イベントリスナをテンプレートに追加) または JavaScript (イベントリスナ関数を記述) で処理できます。次のような HTML アプローチを使用することをお勧めします。

各 tile コンポーネントの HTML (tile.html) には onclick イベントリスナが含まれているため、tile コンポーネントは、ユーザのクリックをリスンします。

<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 でいずれかの tile インスタンスをクリックすると、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 コンポーネントまでチェーニングする必要があります。次にプロパティを下位の detail コンポーネントまで渡します。

この例のファイルでは、次のようになります。

  1. tile.html には onclick イベントリスナがあり、tileClick ハンドラをコールします。
  2. tile.js には tileClick メソッドがあり、詳細値 (this.product.fields.Id.value) が含まれる新しい CustomEvent オブジェクトを作成します。
  3. List.html には ontileClick リスナがあり、handleTileClick ハンドラをコールします。
  4. List.js には handleTileClick メソッドがあり、別のイベントオブジェクト (evt) を作成します。このオブジェクトにも詳細値が含まれます。また、JavaScript でイベントをディスパッチします。
    // Fire the event from c-list 
    this.dispatchEvent(event);
  5. selector.html には onproductselected イベントリスナがあり、handleProductSelected ハンドラをコールします。
  6. selector.js には handleProductSelected メソッドがあり、selectedProductId を evt.detail value に設定します。
  7. detail.html には条件付きディレクティブ (単元 2 を思い出してください) があり、product の値を待機します。
    <template if:true={product}>
  8. detail.js はすべてをまとめます。@track デコレータを使用して productId 値を監視します。次に、get/set パターンを使用して値を取得し、ローカル変数 product に設定すると、detail.html が条件付きコンテンツを読み込みます。

getter と setter はよく使用される JavaScript 構文です。これにより、ロジックと条件をプロパティ割り当てに追加できます。

import { LightningElement, track, api } from 'lwc';
import { bikes } from 'c/data';


export default class Detail extends LightningElement {

    // Ensure changes are reactive when product is updated
    @track 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 ツリーの上位へのイベントの伝搬を管理するためのプロパティがあります。詳細は、「Configure Event Propagation」.(イベント伝搬の設定) を参照してください。デフォルト値の変更は高度なイベント処理向けであり、期待される動作を確認するためのテストが必要です。

組織へのファイルのリリース

では、これらのファイルを Dev Hub 対応組織にリリースしてどう機能するか見てみましょう。前の単元で実行したのと同じ手順を使用して、新しいファイルをリリースし、組織を開き、Lightning アプリケーションビルダーでこのアプリケーションに新しいページを作成します。
  1. ebikes-lwc ディレクトリから、Dev Hub 組織のユーザ名を使用して新しいファイルをリリースします。
    sfdx force:source:deploy -p force-app -u <username>
  2. 組織を開きます。
    sfdx force:org:open -u <username> 
  3. [設定] で、[クイック検索] ボックスに「Lightning アプリケーションビルダー」と入力し、[Lightning アプリケーションビルダー] を選択します。
  4. [新規] をクリックします。
  5. selector コンポーネントを使用して範囲が 1 つのページを作成します。

連携する複数のコンポーネントで構成された完全にインタラクティブなページができました。次は、スタイルを設定し、組織からライブデータを取得します。