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

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

学習の目的

この単元を完了すると、次のことができるようになります。

  • 複数のコンポーネントを含むアプリケーションを作成する。
  • 複雑なコンポーネントのファイル構造を説明する。
  • イベントを処理する。

イベントのジャーニー

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

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

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

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

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

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

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

  1. このアプリケーションのファイルを Trailhead 用自転車セレクタアプリケーションからダウンロードします。
  2. ファイルを bikeCard プロジェクトの 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;
}

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

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

イベントは上へ、プロパティは下へ

複雑なコンポーネント (複数の親コンポーネントと子コンポーネントが含まれるもの) の場合、コンポーネントは階層の上位および下位と通信できます。

情報を上下に渡す親コンポーネントと子コンポーネント。

  1. c-todo-item 子コンポーネントは、c-todo-app 親コンポーネントにイベントをディスパッチします。たとえば、ユーザがボタンをクリックしたら、子が親にイベントオブジェクトを渡し、親がイベントを処理して現在のページを変更できます。
  2. c-todo-app 親コンポーネントは、プロパティを子コンポーネントに渡す、または子コンポーネント内でメソッドを呼び出します。たとえば、親は子コンポーネントのテキスト値を設定、または子コンポーネント内のメソッドを呼び出すことができます。

この通信のしくみを見てみましょう。

情報を上位に渡す

情報は、イベントまたはイベントリスナを使用して上位に渡すことができます。

子コンポーンポーネントはイベントをディスパッチし、親コンポーネントはそれをリスンします。イベントのディスパッチには、子が親コンポーネントに渡すことができるイベントオブジェクトの作成が含まれます。親にはイベントに応答するハンドラがあります。

たとえば、次の子コンポーネントには、CustomEvent() を使用してシンプルイベントオブジェクトを作成する nextHandler() メソッドが含まれており、ユーザが [次へ] ボタンをクリックすると、next という値をディスパッチします。

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

親コンポーネントはこのイベントをリスンします。

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

そして、イベントオブジェクトをイベントハンドラに渡します。

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

情報を下位に渡す

情報は、公開プロパティまたは公開メソッドを使用して下位に渡すことができます。

コンポーネントのプロパティは、@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>

公開プロパティは、プリミティブ値、シンプルオブジェクト、配列を下位に渡すのに適したソリューションです。

また、getter と setter を使用して、プロパティが get または set の場合に一部のロジックを実行することもできます。他のコンポーネントに対して公開するために、@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 でのイベント処理

このセレクタアプリケーションは、ある種別のイベント、つまりユーザによるタイルのクリックを処理する必要があります。このイベントが発生すると、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 メソッドがあり、selectedProductIdevt.detail 値に設定します。
  7. detail.html には条件付きディレクティブ (単元 2 を思い出してください) があり、product の値を待機します。
    <template if:true={product}>
  8. detail.js はすべてをまとめます。非公開変数 _productId を作成し、productId 値の状態を追跡します。次に、get/set パターンを使用して値を取得し、非公開変数 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 ツリーの上位へのイベントの伝搬を管理するためのプロパティがあります。詳細は、イベント伝達の設定」を参照してください。デフォルト値の変更は高度なイベント処理向けであり、期待される動作を確認するためのテストが必要です。

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

では、これらのファイルを Dev Hub 対応組織にリリースしてどう機能するか見てみましょう。前の単元で実行したのと同じ手順を使用して、新しいファイルをリリースし、組織を開き、Lightning アプリケーションビルダーでこのアプリケーションにページを作成します。

  1. プロジェクトファイルをリリースするには、default フォルダを右クリックして、VS Code のコマンドパレットの [SFDX: Deploy This Source to Org (SFDX: 組織にこのソースをリリース)] を選択します。
  2. 組織を開くには、VS Code のコマンドパレットの [SFDX: Open Default Org (SFDX: デフォルトの組織を開く)] を使用します。
  3. [設定] で、[クイック検索] ボックスに「Lightning アプリケーションビルダー」と入力し、[Lightning アプリケーションビルダー] を選択します。
  4. [新規] をクリックします。
  5. selector コンポーネントを使用して範囲が 1 つのページを作成します。

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

リソース