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

コントローラを使用したアクションの処理

学習の目的

この単元を完了すると、次のことができるようになります。
  • ユーザアクションを処理するクライアント側コントローラを作成する。
  • コンポーネント属性の値を参照する。
  • コンポーネントのユーザインターフェースコントロールの値を参照する。
  • ユーザインターフェースを変更するコントローラコードを JavaScript で記述する。

コントローラを使用したアクションの処理

これまで、XML スタイルのマークアップのみを使用してきました。これまで、私たちのコンポーネントの出力を変更する方法は、そのマークアップを変更することだけでした。これまで、私たちのコンポーネントはユーザ入力に応答しませんでした。これまで、JavaScript を記述してきませんでした。

この単元では、すべてが変わります。

始めに簡単なコンポーネントを見てみましょう。この簡単な動作を処理できるようにするには、このコンポーネントで何をする必要があるかを想像してください。

Message of the day (本日のメッセージ): [You look nice today (今日は素敵ですね)] ボタン、[Today is going to be a great day (今日が良い日になりますように)] ボタン

これは helloMessageInteractive です。「何かを実行する」コンポーネントでこれより簡単なコンポーネントは想像できません。短い静的テキスト、メッセージ (現在は空白)、および 2 つのボタンで構成されます。次にコードを示します。

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>Message of the day: {!v.message}</p>
    <div>
        <lightning:button label="You look nice today."
            onclick="{!c.handleClick}"/>
        <lightning:button label="Today is going to be a great day!"
            onclick="{!c.handleClick}"/>
    </div>
</aura:component>

見覚えのあるコードです。私たちは、実際には 2 つの <lightning:button> コンポーネントを helloMessage に追加しただけです。ボタンをクリックすると、本日のメッセージが表示されます。

まだ足りません。このコードを入力し、自分で試してみてください。どちらかのボタンを押すと、エラーメッセージが表示されます。

問題があります。

まず私たちは 1 つの事実を認めなければなりません。Lightning コンポーネントに表示されるすべてのエラーメッセージが必ずしも皆さまが期待するわかりやすいものではないことです。ただし、このメッセージは別です。これは、「handleClick」という名前のコントローラアクションがないことを示します。「handleClick」はどこから取得するのでしょうか。これは、2 つの各 <lightning:button> タグの onclick 属性に割り当てられた式から取得します。

onclick="{!c.handleClick}"

これがボタンの場合、onclick 属性では、ボタンがクリックされたときの動作をボタンに割り当てることを想像できます。ただし、私たちが割り当てたのは、{!c.handleClick} の式です。少し謎めいているかもしれません。

実際はとても簡単な話です。前記の v.message 式と同様に、c.handleClick は、値プロバイダ (c) とプロパティ (handleClick) です。c は、コンポーネントのクライアント側コントローラの値プロバイダであり、handleClick は、そのコントローラに定義された関数です。つまり、{!c.handleClick} は、コンポーネントのコントローラ内のアクションハンドラへの参照です。

c.handleClick: c は、コンポーネントのコントローラの値プロバイダであり、プロパティ handleClick は、そのコントローラに定義された関数である

コントローラとは?

説明いたしますと、コントローラとは、基本的にコードのコレクションであり、「何かが実行された」ときのアプリケーションの動作を定義します。「何か」とは、ユーザ入力、タイマーなどのイベント、データ更新などです。任意の開発者サイトで Model-View-Controller (モデル-ビュー-コントローラ) を参照すると、さまざまな定義を取得できます。私たちの目的 (Lightning コンポーネント向け) では、コントローラは、コンポーネントのアクションハンドラを保持するコンポーネントバンドルのリソースです。アクションハンドラは、特定の関数の署名を持つ JavaScript 関数です。

高度な操作

この単元ではコントローラについて多くのことを説明しますが、私たちはコンポーネント自体はビューであると認識しています。MVC (Model-View-Controller: モデル-ビュー-コントローラ) についても述べましたが、これは、Web アプリケーションフレームワークでとても一般的な設計パターンです。Lightning コンポーネントは MVC パターンに基づいて構築されるのでしょうか?

一言でいえば、違います。確かに類似点はありますが、正確に言うと、Lightning コンポーネントは View-Controller-Controller-Model (ビュー-コントローラ-コントローラ-モデル) またはおそらく View-Controller-Controller-Databas (ビュー-コントローラ-コントローラ-データベース) になります。

このパターン名の中で「controller (コントローラ)」が 2 つ並んで出てくるのはなぜでしょうか。Salesforce とやり取りする場合、この単元で使用したクライアント側のコントローラに加えてサーバ側のコントローラがコンポーネントに含まれるためです。この二重コントローラの設計が、Lightning コンポーネントと MVC の主な違いです。

「モデル」と「データベース」はどのように区別するのでしょうか。従来の MVC では、モデルは、基盤となるデータストレージ (通常はリレーショナルデータベース) と残りのアプリケーションとの間のプログラム的な抽象化 (通常はクラス) です。Lightning コンポーネントでは、@AuraEnabled コントローラメソッドと DML 操作の間を直接的に介在する Apex クラスはありません。ただし、Apex コードと、基盤となるストレージレイヤの間には、sObject がすでに抽象化として存在します。計算項目や入力規則ロジックを追加できるほか、完全なプログラム動作をトリガの形式で追加することもできます。では、これはデータベースまたはモデルのどちらでしょうか。「ポ-タイ-トウ」と言う言葉を「ポ-タア-トウ」と言うともっと格好良くなります。

途方に暮れていますか? ワクワクしますか? サーバ側コントローラについての詳細は、後の単元で説明します。

では、helloMessageInteractive コントローラについてさらに詳しく見て行きましょう。少し具体的に説明します。

({
    handleClick: function(component, event, helper) {
        let btnClicked = event.getSource();         // the button
        let btnMessage = btnClicked.get("v.label"); // the button's label
        component.set("v.message", btnMessage);     // update our message
    }
})

コントローラリソースは興味深い形式を持ちます。これは、名前-値のペアの対応付けが含まれる JavaScript オブジェクトです。ここで、名前はアクションハンドラの名前であり、値は関数定義です。

アクションハンドラ

名前-値ペアと特定の関数署名の組み合わせがアクションハンドラです。これから、アクションハンドラ、コントローラアクション、コントローラ関数という用語が出てきますが、これらは同じ意味で使用されます。これは、ほとんどの場合で当てはまります。どの用語も意味するものはほぼ同じです(このモジュールではこの例外について心配していません)。

コントローラリソースの特殊な形式について過剰に心配する必要はありません。開発者コンソールで [CONTROLLER] ボタンをクリックすると、サンプルのアクションハンドラがすでに追加されたコントローラリソースを取得できます。注意点として、アクションハンドラの間にカンマを挿入する必要があります。これを忘れると構文エラーになります。これは JavaScript の基本構文ですが、仕様について少し見てみます。

実際の handleClick 関数は 4 行のコードのみですが、初めはわかりづらいように思える場合があります。概要レベルではこれは簡単です。ボタンをクリックすると、そのアクションハンドラがコールされます (1)。アクションハンドラでは、コントローラが、クリックされたボタンを取得し、そのボタンからラベルテキストを取り込んで、コンポーネントの message 属性をそのテキストに設定します (2)。次に、本日のメッセージが更新されます (3)。You look nice today (今日は素敵ですね)!

アクションハンドラでは、コントローラが、クリックされたボタンのテキストを取得してコンポーネントの message 属性を設定する

簡単でしょう? というのは ...

以下は、とても重要なことなので 1 行ずつ詳しく説明します。

handleClick: function(component, event, helper) {

アクションハンドラ名の後に匿名関数宣言が続きます。ここで重要なことは関数の署名です。これは技術的には必要ではありませんが、コントローラ関数を常に宣言してこの 3 つのパラメータを取る必要があります。このパラメータについては後で詳しく説明しますが、ここでは、このパラメータが何を表すかを説明します。

  • component — コンポーネント。この場合は、helloMessageInteractive です。
  • event — アクションハンドラをコールする原因となったイベント。
  • helper — コンポーネントのヘルパー。再利用可能な関数の別の JavaScript リソースです。
    let btnClicked = event.getSource();         // the button

handleClick<lightning:button> タグとその onclick 属性に接続されていることを思い出してください。event は、誰かがボタンをクリックする操作です。イベント内にはソース (source) の概念があります。これは、イベントを生成したもの、つまりボタン自体を表します。したがって、event.getSource() をコールすると、クリックされた特定の <lightning:button> への参照が取得されます。

    let btnMessage = btnClicked.get("v.label"); // the button's label

ボタンへの参照を取得しました。ここで何をしましょう。参照の内部にあるラベルを取得します。これは、コンポーネントマークアップの <lightning:button> に設定されています。たとえば、<lightning:button label="You look nice today." ... > などです

もう少し詳しく説明します。手元に <lightning:button> の定義はありませんが、label は別の属性であり、helloMessageInteractive に追加した message 属性によく似ています。任意のコンポーネントで get() をコールし、取得する属性の名前を v.attributeName の形式で指定できます。結果は属性の値です。

コンポーネントマークアップの場合と同様に、v はビュー、つまりコンポーネント自体を表しますが、この場合は子コンポーネント <lightning:button> です。helloMessageInteractive ではありません。次のように考えてください。コンポーネント btnClicked が何であろうと btnClicked.get("v.label") がそのコンポーネントの肩をたたき、「v.label をください」と言います。このコンポーネントは「v は自分だ」と考え、自分の中を見て、そのラベル属性の値を返します。

これでボタンからテキスト文字列が取得されました。もう 1 ステップ残っています。そのステップで、message 属性を新しいメッセージテキストに変更します。当然ですが、get() がコンポーネントの値を参照するのと同じように、set() は値を書き込みます。

    component.set("v.message", btnMessage);     // update our message

ただし、重要な違いが 1 つあります。get() を btnClicked に対してコールしましたが、これは helloMessageInteractive の内部にある <lightning:button> です。set() は、helloMessageInteractive コンポーネント自体である component に対してコールします。以上は、作成したほぼすべてのコンポーネントで繰り返すパターンです。つまり、コンポーネントから値を取得し、おそらく何かの処理を行って、コンポーネント自体に値を設定します。

Aura コンポーネントの View-Controller (ビュー-コントローラ) プログラミングモデル

状況を確認しましょう。理解しましたか。本当ですか。理解したと思ったら、前述のコードを使って helloMessageInteractive コンポーネントを実際に作成してください。これは 1 つのコンポーネントであり、コードのコピー/貼り付けの時間は 2 分ほどですが、これを機能させるには処理アクションを理解する必要があります。

ここでやったことは非常に簡単です。コードの行数がそれほど多くないためです。ただし、Aura コンポーネントを使ったアプリケーション構築の基本概念の一部がこれだけのコードで表されています。

コンポーネントをアクションハンドラに接続することを「結び付けること」と考えることができます。helloMessageInteractive を簡単な電気回路として考えてみてください。そこにはスイッチと電球があります(Lightning コンポーネントフレームワークが電力を供給します)。スイッチ自体は素敵なクリック音を出すかもしれませんが、結び付けられるまでスイッチはまったく機能しません。流行のエジソンスタイルの電球を持っていても、結び付けられていなければ何も照らすことができません。

Aura コンポーネントでも同じことが言えます。前に、コンポーネントバンドル内のさまざまなリソースが互いに「自動的に結び付けられる」と述べました。これは本当です。この結び付けは、v および c の値プロバイダの形式を取ります。これらは自動的に作成され、コンポーネント内で使用できるため、コントローラでコンポーネントを参照したり、コンポーネントでコントローラを参照したりできます。ただし、この自動的な結び付けは、高レベル (helloMessageInteractive) でのみ、つまり、コンポーネントの .cmp リソースとコントローラの .js リソース間でのみ行われます。これは、次の図の緑の矢印で示されています。

helloMessageInteractive とそのコントローラが自動的に結び付けられる

特定の <lightning:button> コンポーネントを特定のアクションハンドラに結び付けることは、イベントを生成するもの (ボタンなど) を、イベントを処理するもの (特定のコントローラ関数など) に結び付けることであり、この結び付けは自分で行う必要があります。あなたは実際にこの結び付けを完了しています。機能する回路を完成させるには、赤の矢印が必要です。

{!c.handleClick}<lightning:button> コンポーネントの onclick 属性に追加する (1) と、特定のアクションハンドラに結び付けられます。component.set("v.message", newMessage) (2) をコールすると、そのアクションハンドラの結果がコンポーネントの message 属性に結び付けられます。この属性自体は {!v.message} 式に結び付けられます。

さらに、onclick イベントについては、作成した回路を流れる電子として考えることができます。完全な回路を作成しなければ、イベントはどこにも行く場所がなく、何も起きません。独自のコンポーネントを開始するときは、次の点に留意してください。完全な回路があるか? 本当にあるか? 疑わしい場合は、ホワイトボードや紙にすべてを書き出して、すべての接続を確認することが役立つことがあります。

コンポーネント、イベント、ハンドラを低レベルおよび高レベルで結び付けていると、電子になった気分になります(または、フレームワークの名前、おそらく「ベン・フランクリン」という名前を付けられます)。ものを結び付けることは、Aura コンポーネントプログラミングモデルの基礎です。

もう少し練習してみましょう。結局は練習あるのみです。

関数のチェーニング、結び付け直し、簡単なデバッグ

handleClick の最初のバージョンは 3 行のコードでした。各ステップを「取得-処理-設定」パターンで別々の行に分割したためです。関数のチェーニングと呼ばれるものを使用すると、これをさらに少ない行にまとめることができます。他の Lightning コンポーネントコードにこれが含まれている可能性があるため、試しにやってみましょう。次のコードを helloMessageInteractive コントローラの handleClick の後に追加します。

    handleClick2: function(component, event, helper) {
        let newMessage = event.getSource().get("v.label");
        component.set("v.message", newMessage);
    },
    handleClick3: function(component, event, helper) {
        component.set("v.message", event.getSource().get("v.label"));
    }

あらまあ! 保存するときに構文エラーが発生しませんでしたか。前に言ったことを思い出してください。アクションハンドラ間には、カンマを追加する必要があります。これが問題になっています。前述のスニペットの handleClick2 の最後に示されているように、handleClick の最後の中括弧 (「}」) の後にカンマを追加してください。

このアクションハンドラは、少ないコードで handleClick とまったく同じことを実行します。このため、このアクションハンドラでは、中間変数がスキップされ、多くの場合は次の関数コールが直接「チェーニング」され、そのコールが前のコールの最後にピリオドで区切って追加されています(handleClickhandleClick2 の違いを確認する場合、この概念が違いを最も明確に示しています)。

どのスタイルを使用するかは、個人の好み、おそらくは組織のコーディングスタイルの問題になります。謙虚な著者は handleClick2 がお気に入りです。getset が分かれますが、ボタンの変数に煩わされることがありません。そのラベルテキストが必要なだけです。

当然のことながら、onclick 属性を {!c.handleClick2} または {!c.handleClick3} に設定し、新しいアクションハンドラの 1 つを使用するように <lightning:button> を結び付けるまで、新しいアクションハンドラが正常に動作することを確認できません。これを、1 つの電球から配線を外して別の電球に配線することとして考えることができます。これだけで完了です。

この時点でアプリケーションを再ロードします。結び付け直されたボタンの 1 つをクリックすると、...。仕様どおりです。どのアクションハンドラがコールされるかもわかりません。

シンプルが一番なときもあります。アクションハンドラ関数の 1 つにログ機能を追加してみましょう。

    handleClick2: function(component, event, helper) {
        let newMessage = event.getSource().get("v.label");
        console.log("handleClick2: Message: " + newMessage);
        component.set("v.message", newMessage);
    },

<lightning:button>handleClick2 に結び付けると、このボタンをクリックするたびにブラウザの JavaScript コンソールにログメッセージが表示されます。

より洗練されたデバッグツールはありますが、情報をコンソールに表示することは昔からのデバッグ技術です。何らかのオブジェクトを出力する場合は、オブジェクトを JSON.stringify(yourObject でラップして、さらに有用な詳細情報を取得します。少し覗いてみるだけの場合は、console.log() を使用できます。

より洗練されたデバッグツールと技術についてここでは説明しませんが、すばらしいツールとその使用法については「リソース」を参照してください。

helloMessageInteractive は、とてもシンプルで、ハードコードされた (あくまでも前向きな) 属性を持っています。次の単元ではもう少し複雑なものを使って作業し、実際のユーザ入力を取得する方法を学習します。現実世界の人は常にそれほど前向きであるとは限りません。人が入力したものを検証する方法についても学習します。