AuraとLWCで同じ画面を作成する
練習内容のアウトプット
在庫管理システムを Aura Component/LWC + Apexで作成
初級編は「商品名/品番/在庫数のリスト表示」をテーマに作成します。
目的
Lightning ページに配置すると、初期表示で「商品名/品番/在庫数」が一覧表示される。
まだ「登録」「編集」「削除」などの操作はなし。
画面側の記述(Aura Component/LWC)、js、Apex の三つを記述してそれぞれがどう関係しているかを理解する事。
設計書イメージ(初級編)
対象機能
・商品マスタ(Product__c カスタムオブジェクト想定)
・項目:
・商品名(Name)
・品番(ProductCode__c)
・在庫数(Stock__c)
画面仕様
・Aura Component/LWCでリスト表示
・SOQL を用いて Apex Controller からデータ取得
・画面側表示で全商品をリスト表示
3つの役割イメージ(Aura/LWC, JS, Apex)
1,Aura Component/LWC (.cmp, .html相当) 見た目・操作部分
・画面定義している部分(UI部分)
・ユーザーが直接、操作したり見る部分(入力欄、ボタン、表など)
2,Controller.js (クライアントサイドJS) イベント処理やデータ受け渡し
・画面側(Aura/LWC)とApexを繋ぐ仲介役
・ボタン押下や画面初期表示などのイベントを受け取る部分
・必要ならApexを呼び出してデータを取ってきたり、画面に結果を反映させる
3,Apex Controller (サーバサイドApex) DB操作
・Salesforceの データベース処理 を実際に行う
・SOQLでデータを取得、DMLで更新・削除を行う
・AuraやJSはデータベースに直接触れないので、必ずApexを経由する
クライアントサイドとは
クライアント = ユーザーが使うPCやブラウザ(Chrome, Edge など)
クライアントサイド = ブラウザの中で動く処理
ボタンが押されたときに反応したり、入力欄の文字を取得したり画面のイベント処理を担当する。ユーザーのPC(ブラウザ)の中で動く JavaScript という事になるらしい
サーバサイドとは
サーバ = Salesforceのクラウド(自分のPCではなくSalesforceが用意しているコンピュータ)
サーバサイド = Salesforce側で実行される処理 Apexは Salesforceサーバー側で動きます
Salesforceのクラウド上で動く処理(DB操作も含む)をサーバサイドApexといいます。
サーバサイドApexを経由して、Salesforceのデータベースにデータを「送る」「取る」「更新する」のがデータベース処理です。
Aura Componentでの記述内容
public with sharing class InventoryController {
// @AuraEnabled を付けて画面から呼べるのはstaticメソッドだけ(newでの作成はしない)
@AuraEnabled(cacheable=true)
public static List<Product__c> getInventoryList() {
return [
SELECT Id, Name, ProductCode__c, Stock__c
FROM Product__c
ORDER BY Name
];
}
}
<aura:component controller="InventoryController" implements="force:appHostable,flexipage:availableForAllPageTypes" access="global">
<!-- 変数定義 -->
<aura:attribute name="inventoryList" type="Product__c[]"/>
<!-- 初期化処理(画面を開いた時doInitを実行するという意味) -->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<lightning:card title="在庫一覧">
<aura:iteration items="{!v.inventoryList}" var="item">
<p>{!item.Name} / {!item.ProductCode__c} / 在庫数: {!item.Stock__c}</p>
</aura:iteration>
</lightning:card>
</aura:component>
({
doInit : function(component, event, helper) {
var action = component.get("c.getInventoryList");
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
component.set("v.inventoryList", response.getReturnValue());
} else {
console.error("エラー: " + response.getError());
}
});
$A.enqueueAction(action);
}
})
全体の処理の流れを整理すると:
1,Apexクラス InventoryController に在庫リスト取得メソッド getInventoryList() を定義
・@AuraEnabled でAura/LWC両方から呼び出し可能
・cacheable=true は画面表示用の参照データに使うときのベストプラクティス
2,Aura Component側で doInit をハンドリングして初期処理を走らせる
3,JSコントローラから getInventoryList を呼び出して、取得結果を v.inventoryList にセット
4,aura:iteration でリストを表示
記述ポイント
action.setCallback(this, function(response) {
// 成功 or 失敗した時の処理
});
・これは「Apex呼び出しの結果が返ってきたら、この処理を実行してね」と登録している部分です。
・第一引数の this → コールバック関数のスコープ(おまじないみたいなもの)。
・第二引数の function(response) → 実際の処理を書く関数。
ここで response というのは Apex から返ってきたデータが入ってくる引数です。
・response.getState() → 成功したか失敗したかの状態を取得
・response.getReturnValue() → Apexから返ってきたデータ
$A.enqueueAction(action);
・$A は Aura フレームワークのグローバルオブジェクト。
・enqueueAction は「この action(Apex呼び出しリクエスト)をキューに入れて実行してね」という命令。
・これを呼んで初めて Apex が実行されます。
LWCでの記述内容
public with sharing class InventoryController {
@AuraEnabled(cacheable=true)
public static List<Product__c> getInventoryList() {
return [
SELECT Id, Name, ProductCode__c, Stock__c
FROM Product__c
ORDER BY Name
];
}
}
@ AuraEnabled
→ LWC や Aura から呼べるように公開しているアノテーション。
(cacheable=true)
→ データが変わらない(参照系の処理)ときにキャッシュを使える設定。逆に更新(insert/update/delete)が絡む処理では cacheable=false にする必要があります。
<template>
<lightning-card title="在庫一覧">
<template if:true={inventoryList.data}>
<template for:each={inventoryList.data} for:item="item">
<p key={item.Id}>
{item.Name} / {item.ProductCode__c} / 在庫数: {item.Stock__c}
</p>
</template>
</template>
<template if:true={inventoryList.error}>
<p>データ取得エラー: {inventoryList.error}</p>
</template>
</lightning-card>
</template>
import { LightningElement, wire } from 'lwc';
import getInventoryList from '@salesforce/apex/InventoryController.getInventoryList';
export default class InventoryList extends LightningElement {
// @wire を使って Apex の結果を自動で取得
@wire(getInventoryList) inventoryList;
}
'@salesforce/apex/InventoryController.getInventoryList';
→ Apex の InventoryController クラスの getInventoryList メソッドを呼び出せるようにしている。
@wire(getInventoryList) inventoryList;
→ LWCフレームワークが「画面が表示されたときに自動でこのメソッドを呼び出して、結果を inventoryList に入れてくれる」 という仕組み。
@wire(getInventoryList)
→ Apex の getInventoryList メソッドを呼ぶ設定。
inventoryList;
→ その呼び出し結果が入る変数(プロパティ)この中に以下の2種類の値が入ります
成功時 → inventoryList.data にレコード一覧
失敗時 → inventoryList.error にエラー内容
@wire の動作イメージ
1,LWCコンポーネントが画面にロードされる
2,フレームワークが@wire(getInventoryList)を自動で呼ぶ(自動でApex呼び出しが走る)
3,Apex が SOQL を実行してDBからリストの内容を返す
4,返ってきたデータが画面側の inventoryList.data に入る
5,もしエラーなら inventoryList.error に入る
今回の核となる記述:イメージ、考え方
Apexのメソッド = 「倉庫から在庫を持ってくる係」
static = 係を雇う(インスタンス化する)必要がなく、倉庫に電話一本で呼び出せる状態(LWC、Auraでの記述では@AuraEnabledで呼ぶので、基本的にはインスタンス化しないという認識でOK)
@wire = 「画面側のLWC、Auraが開いた瞬間、自動で倉庫に電話して在庫リストを取り寄せてくれる仕組み(Apex呼び出しは非同期処理)」
inventoryList = 届いた荷物(データ)が入る箱を@wire(getInventoryList) inventoryList;この記述で定義している。
まとめ
Apex:SalesforceのDBからデータを取ってくる係
@wire:指定のApexメソッドを自動で呼んで結果をプロパティに入れてくれる魔法の仕組み
JSの inventoryList:成功したら inventoryList.data にレコードリストが入る。失敗したら inventoryList.error にエラー情報が入る
Aura と LWC の違い・ポイント(主要な記述の差)
・Apex呼び出し
Aura → component.get("c.method") + $A.enqueueAction
LWC → @wire デコレーター または imperative call (await callApex())
・データ保持
Aura → aura:attribute に定義
LWC → JSクラスのプロパティ(@track や普通の変数)
・初期処理
Aura → で明示的に実行
LWC → @wire で自動取得 or connectedCallback() で手動処理
・Apex処理の呼び出し
Aura → $A.enqueueAction(action); = Aura 限定の非同期実行命令
LWC → LWCでは上記記述は使わない(Promiseの命令形呼び出し または @wire で自動的に非同期処理される)
・繰り返し処理
Aura → aura:iteration
LWC → template for:each
・エラーハンドリング
Aura → コールバックで state を判定
LWC → @wire の .data / .error プロパティで分岐
補足
非同期処理について
Apex呼び出しは、呼んだ瞬間に結果が返るわけではありません。
結果が出るまで時間がかかるので、「待たずに先に進む」という仕組みになっています。
再レンダリングについて
変数内の値が変更された時に、その値を画面側に表示している場合は、自動更新されて値が変更された値に更新される仕組みをレンダリングという。Auraでも同じく 属性 (aura:attribute) の値が変わると自動更新されるが、内部的に処理を増やす必要があるので、LWCより少し複雑、とだけ覚えておくと良い。
プロパティの変更=画面の自動更新トリガー
おまけ:@wire と Imperative Call(命令型呼び出し)の違い
@wire
→ 自動呼び出し(勝手に実行される)
imperative call
→ 明示的に getInventoryList() を書いて呼ぶ(命令する)
「Apexメソッドを 自分で命令して呼び出す」スタイルを総称して imperative call と呼ぶ
import { LightningElement } from 'lwc';
import getInventoryList from '@salesforce/apex/InventoryController.getInventoryList';
export default class InventoryList extends LightningElement {
inventoryList = [];
// イベント発生時に実行される処理を定義しているメソッド
handleClick() {
// この部分が「imperative(命令型)」です
getInventoryList()
.then(result => {
this.inventoryList = result;
})
.catch(error => {
console.error(error);
});
}
}
処理の流れ
1、getInventoryList()でApexを呼び出す
2、呼び出したApexの結果がまだ帰ってきていなくてもjsの処理は先に進める
3、結果が返ってきたら.then(...)の中身が実行される
4、エラーが出たら.catch(...)の中身が実行される
5、this.inventoryList に代入されるとレンダリングが走り、画面に表示される