LWCを実装したいけど、デコレータ(@〇〇)って何?

2023/03/20に公開

はじめに

Lightning Web Component を実装しようと思ったが、
どんなファイルが必要で、ファイル連携をどうすればいいのかがさっぱりわからなかったので、
少しずつ紐解いていければと思います。
今回は主に@apiデコレータのことについて触れています。

この記事でわかること

  1. デコレータとは何か
  2. LWCにおけるデコレータの種類と使い方

まずはLWCの構成ファイルの確認

こちらは公式学習サイトのTrailheadより

Lightning Web コンポーネントは、3 ステップで作成できます。
(1) JavaScript ファイルを作成し、
(2) HTML ファイルを作成し、必要に応じて
(3) CSS ファイルを作成します。

HTML は、コンポーネントの構造を提供します。
JavaScript は、コアビジネスロジックとイベント処理を定義します。
CSS は、コンポーネントのデザインとアニメーションを提供します。

それぞれのファイルをどうやって連携させるのか

それぞれのファイルで
画面の構造を作り、データもロジックも組んだしても、
それぞれのファイルが単独で機能していてはコンポーネントは出来上がりません。
時計で例えるなら、

  • 文字盤や指針などの枠組み -HTML
  • 動く仕組み -javaScript

これらがなければ時計として機能しないのと同じです。
それらをまとめるためには、何かしら、固定具や接着剤、電極的なものでそれぞれを繋げますよね。
LWCも同じように機能を繋ぐ便利アイテムがあるらしいんです。

それがデコレータと呼ばれるものです。

LWCにおけるデコレータの種類と使い方

LWCのデコレータは
@api, @track, @wire の3つが用意されています。

@track :

   コンポーネント内のJavaScriptとHTMLを連携するためのデコレータ。
   Spring '20以降では不要となったらしく、現在は使わなくても連携可能。

@wire :

公式開発ガイドより

ワイヤーサービスがデータを提供すると、>コンポーネントは再レンダリングします。
コンポーネントは、>JavaScriptクラスで@wireを使用して、
ワイヤアダプタまたはApexメソッドを指定します。

引用サイトより

JavaScriptの変数とApexのメソッドを紐づけたいときに使用します

要するに@wireはマークしたメソッドを自身のコンポーネントだけで使用できる。
privateと同じようなものと捉えて良さそう。
Apexクラスのメソッドを指定できる。

@api :

Trailheadより引用

項目を公開としてマークします。
公開プロパティではコンポーネントの API を定義します。
HTML マークアップでコンポーネントを使用する所有者コンポーネントは、
コンポーネントの公開プロパティにアクセスできます。
すべての公開プロパティはリアクティブです。
つまり、フレームワークはプロパティの変更を監視します。
プロパティの値が変更されると、フレームワークはそれに反応し、コンポーネントを再表示します。

ん?ちょっとよくわからなかったので、開発ガイドでも確認。

パブリックプロパティを公開するには、フィールドを@apiで装飾します。
パブリックプロパティは、コンポーネントのAPIを定義します。

パブリックメソッドを公開するには、メソッドを@apiで装飾します。
パブリック・メソッドは、コンポーネントのAPIの一部です。

オーナーコンポーネントや親コンポーネントは、> 子コンポーネントのJavaScriptメソッドを呼び出して、
格納階層を越えて通信することができます。

んー、プロパティとメソッドをpublicに扱うためには@apiでデコレートする必要があるんだな。
オーナーや親コンポーネント側は子のコンポーネントのJavaScriptのメソッドを使えるんだな。

わかるような、わからないような、、、
例えば??
もう少し踏み込んで調べてみよう。
開発者ガイドの例をみてみる。

@apiデコレータの詳細

結局はこういうこと(イメージ)

いろいろ紐解いた結果、こういうイメージになりました。
後述するテキストが少しでも読みやすくなるようにと思い、先にイメージを載せておきます。

では、開発者ガイドのコードを参考に話を進めていこうと思います。

■ プロパティを他のコンポーネントでも使えるようにする

プロパティ [Public Properties]

https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.reactivity_public

今回はitemNameを公開のプロパティとして扱います。

JavaScriptで定義したitemNameは同じコンポーネントで使うことはできます。
ここではtodoItemコンポーネントのJavaScript.jsで指定したプロパティ設定を同コンポーネントのtodoItem.htmlで使っています。

JavaScriptのコードスニペット末尾で
本題のitemNameを@apiを使って外部コンポーネントからも使えるようにします。

// todoItem.js
import { LightningElement, api } from 'lwc';
export default class TodoItem extends LightningElement {
    @api itemName = 'New Item';
}
<!-- todoItem.html -->
<template>
    <div class="view">
        <label>{itemName}</label>
    </div>
</template>

下記の例では、todoItemコンポーネントで設定したプロパティをtodoAppでも扱えるようにしています。
外部コンポーネントを使ってプロパティを設定する場合は
<c-(コンポーネント名) (プロパティ名)="Value"></c-(コンポーネント名)>
となります。

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

また、HTMLで表示させたい要素の構造をJavaScriptで設定する場合は、以下のようにして使うことができます。

// todoApp.js
myItem = this.template.querySelector('c-todo-item').itemName;

これはc-todo-itemコンポーネントで指定した要素のインスタンス(itemName)にアクセスをしています。


■ メソッドを他のコンポーネントでも使えるようにする

メソッド[Call Methods on Child Components]

https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_javascript_methods

メソッドもプロパティと同じ様な使い方でした。
使用元のメソッドやプロパティに@apiデコレータをつけて、
他のコンポーネントで使う場合は使用元のDOM要素を指定し、メソッドを記載すれば使用できるようになります。

今回はplayメソッドを公開のメソッドとして扱います。
他のコンポーネントでメソッドを使用する場合の手順は
 1. コンポーネントのDOM要素を指定
 2. そこに存在するメソッドを".〇〇"の形で記載
 3. それを自身のコンポーネントでメソッド化
だけです。

なので、公開したいplayメソッドに@apiをデコレートされています。

// videoPlayer.js
import { LightningElement, api } from 'lwc';

export default class VideoPlayer extends LightningElement {
    @api videoUrl;
    @api
    get isPlaying() {
        const player = this.template.querySelector('video');
        return player !== null && player.paused === false;
    }
    @api
    play() {
        const player = this.template.querySelector('video');
        // the player might not be in the DOM just yet
        if (player) {
            player.play();
        }
    }
    pause() {
        const player = this.template.querySelector('video');
        if (player) {
            // the player might not be in the DOM just yet
            player.pause();
        }
    }
    // private getter for computed value
    get videoType() {
        return 'video/' + this.videoUrl.split('.').pop();
    }
}

ちなみこのサンプルコードでは
videoUrlに@apiデコレータがついていますが、
videoUrlはpublicプロパティとしており
どのコンポーネントでもvideoUrlという項目を使うことができるようになっています。

videoUrlを同じコンポーネント使いたい場合は、コンポーネントの指定をせず、キャメルケースで記載すると使えるようになります。
ちなみに、srcは、メディアのリソースの場所であり、 <audio> および <video> では必須

<!-- videoPlayer.html -->
<template>
    <div class="fancy-border">
        <video autoplay>
            <source src={videoUrl} type={videoType} />
        </video>
    </div>
</template>

以下は

  • videoPlayerコンポーネントの@apiでデコレートされたplayメソッドを、
  • methodCallerコンポーネントでは、handlePlayメソッドとして扱えるようにしています。
// methodCaller.js
import { LightningElement } from 'lwc';

export default class MethodCaller extends LightningElement {
    video = "https://www.w3schools.com/tags/movie.mp4";

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

また、methodCallerコンポーネントのHTMLでhandlePlayメソッドを使えるボタンを作った場合は以下の様になります。

<template>
    <div>
        <c-video-player video-url={video}></c-video-player>
        <button onclick={handlePlay}>Play</button>
    </div>
</template>

@apiデコレータは要するに

@apiでマークしたメソッドやプロパティを
他のコンポーネントのJavaScriptでも使用できるようにするものなんですね!!

参考(引用)文献

  • 開発者ガイド ~Decorators~

https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.reference_decorators

  • デコレータについてもわかりやすいイラストがあったので参考・引用させていただきました

https://base.terrasky.co.jp/articles/zpT4b

Discussion