参照
Lightning web componentにおけるデータ取得/更新/登録のやり方を記載する。
内容
getRecordを使ったデータ取得
<template>
<lightning-card>
<div class="slds-m-around_medium">
<!-- 1.0 -->
<template if:true={account.data}>
<div>
<p>Name:{name}</p>
</div>
<div>
<p>Phone:{phone}</p>
</div>
<div>
<p>Annual Revenue:{account.data.fields.AnnualRevenue.value}</p>
</div>
</template>
</div>
</lightning-card>
</template>
※1.0:if:true={account.data}は必須。これがないとエラーになる。画面が描画された時はaccount.dataに値が入っていないため。
import { LightningElement, api,wire } from 'lwc';
import { getRecord,getFieldValue } from 'lightning/uiRecordApi';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';
import ACCOUNT_PHONE from '@salesforce/schema/Account.Phone';
export default class Sample1 extends LightningElement {
@api recordId;
// 1.0
@wire(getRecord, { recordId: '$recordId', fields: [ACCOUNT_NAME, ACCOUNT_PHONE,'Account.AnnualRevenue'] })
account;
get name() {
// 2.0
return getFieldValue(this.account.data, ACCOUNT_NAME);
}
get phone() {
// 3.0
return this.account.data.fields.Phone.value;
}
}
※1.0:getRecordを使うとレコードを取得できる。第一引数にレコードのID/第二引数に取得するフィールドを指定。getRecordをimportするのを忘れずに。
※2.0:getFieldValueを使うと、wireサービスで取得した値から欲しいフィールドを取得できる
※3.0:getFieldValueを使わない場合の値の取り方。valueを忘れずに。特別な理由がないかぎり、getFieldValueを使う。
getRecordを使ったデータ取得(例外処理付き)
<template>
<lightning-card>
<div class="slds-m-around_medium">
<template if:true={account}>
<div>
<p>Name:{name}</p>
</div>
<div>
<p>Phone:{phone}</p>
</div>
<div>
<p>Annual Revenue:{account.fields.AnnualRevenue.value}</p>
</div>
</template>
</div>
</lightning-card>
</template>
import { LightningElement, api,wire,track } from 'lwc';
import { getRecord,getFieldValue } from 'lightning/uiRecordApi';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';
import ACCOUNT_PHONE from '@salesforce/schema/Account.Phone';
export default class Sample2 extends LightningElement {
@api recordId;
// 2.0
@track account;
// 1.0
@wire(getRecord, { recordId: '$recordId', fields: [ACCOUNT_NAME, ACCOUNT_PHONE,'Account.AnnualRevenue'] })
wiredAccount({ data, error }) {
if (data) {
this.account = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.account = undefined;
}
}
get name() {
return getFieldValue(this.account, ACCOUNT_NAME);
}
get phone() {
return this.account.fields.Phone.value;
}
}
※1.0:wireサービスの結果は、変数に渡すこともできる。
※2.0:構造体(オブジェクト)には、@trackを付ける。これを付けないと、構造体はリアクティブにならない。
updateRecordを使ったデータ更新
<template>
<lightning-card>
<div class="slds-m-around_medium">
<template if:true={account.data}>
<lightning-input label="Name" value={account.data.fields.Name.value} data-field="Name" class="slds-m-bottom_x-small"></lightning-input>
<lightning-input label="Phone" value={account.data.fields.Phone.value} data-field="Phone" class="slds-m-bottom_x-small"></lightning-input>
<lightning-button label="Update Account" variant="brand" onclick={updateAccount}></lightning-button>
</template>
</div>
</lightning-card>
</template>
import { LightningElement, track, wire, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord,updateRecord } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import Phone_FIELD from '@salesforce/schema/Account.Phone';
import ID_FIELD from '@salesforce/schema/Account.Id';
export default class Sample3 extends LightningElement {
@api recordId;
@track error;
@wire(getRecord,{recordId: '$recordId',fields: [NAME_FIELD,Phone_FIELD]})
account;
// 1.0
updateAccount() {
// バリデーションチェック
const allValid = [...this.template.querySelectorAll('lightning-input')]
.reduce((validSoFar, inputFields) => {
inputFields.reportValidity();
return validSoFar && inputFields.checkValidity();
}, true);
if (allValid) {
const fields = {};
fields[ID_FIELD.fieldApiName] = this.recordId;
fields[NAME_FIELD.fieldApiName] = this.template.querySelector("[data-field='Name']").value;
fields[Phone_FIELD.fieldApiName] = this.template.querySelector("[data-field='Phone']").value;
const recordInput = { fields };
// 2.0
updateRecord(recordInput)
.then(() => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Contact updated',
variant: 'success'
})
);
})
.catch(error => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Error updating record',
message: error.body.message,
variant: 'error'
})
);
});
}
else {
this.dispatchEvent(
new ShowToastEvent({
title: 'Something is wrong',
message: 'Check your input and try again.',
variant: 'error'
})
);
}
}
}
※1.0:html側のボタンを押したときに動くメソッド。html側のボタンのonclick属性で指定されている。
※2.0:updateRecordを使ってレコードを更新できる。importするのを忘れずに。
createRecordを使ったデータ登録
<template>
<lightning-card>
<div class="slds-m-around_medium">
<template if:true={account.data}>
<lightning-input label="Name" value={account.data.fields.Name.value} data-field="Name" class="slds-m-bottom_x-small"></lightning-input>
<lightning-input label="Phone" value={account.data.fields.Phone.value} data-field="Phone" class="slds-m-bottom_x-small"></lightning-input>
<lightning-button label="Create Account" variant="brand" onclick={createAccount}></lightning-button>
</template>
</div>
</lightning-card>
</template>
import { LightningElement, track, wire, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord,createRecord } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import Phone_FIELD from '@salesforce/schema/Account.Phone';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
export default class Sample4 extends LightningElement {
@api recordId;
@track error;
@wire(getRecord,{recordId: '$recordId',fields: [NAME_FIELD,Phone_FIELD]})
account;
createAccount() {
const allValid = [...this.template.querySelectorAll('lightning-input')]
.reduce((validSoFar, inputFields) => {
inputFields.reportValidity();
return validSoFar && inputFields.checkValidity();
}, true);
if (allValid) {
const fields = {};
fields[NAME_FIELD.fieldApiName] = this.template.querySelector("[data-field='Name']").value;
fields[Phone_FIELD.fieldApiName] = this.template.querySelector("[data-field='Phone']").value;
const recordInput = { apiName: ACCOUNT_OBJECT.objectApiName,fields };
// 1.0
createRecord(recordInput)
.then(() => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Contact updated',
variant: 'success'
})
);
// return refreshApex(this.account);
})
.catch(error => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Error creating record',
message: error.body.message,
variant: 'error'
})
);
});
}
else {
this.dispatchEvent(
new ShowToastEvent({
title: 'Something is wrong',
message: 'Check your input and try again.',
variant: 'error'
})
);
}
}
}
※1.0:craeteRecordでレコードを作成する。importを忘れずに。基本的にupdateRecordと同じだが、apiNameの指定が必要な点が異なる。
Apexメソッドを呼び出してデータ取得/更新
public with sharing class AccountController {
// 1.0
@AuraEnabled(cacheable=true)
public static Account getSingleAccount(String id) {
return [
SELECT Id, Name, Phone
FROM Account
WHERE Id = :id
WITH SECURITY_ENFORCED
];
}
// 2.0
@AuraEnabled(cacheable=false)
public static Account updateSingleAccount(Account account){
update account;
return account;
}
}
※1.0:AuraEnabledを付けるとLWCから呼び出せるようになる。データ取得系のメソッドにはcacheable=trueを付ける(推奨)
※2.0:AuraEnabledを付けるとLWCから呼び出せるようになる。データ更新系のメソッドにはcacheable=falseを付ける(必須)
<template>
<lightning-card>
<div class="slds-m-around_medium">
<template if:true={account.data}>
<lightning-input label="Name" value={account.data.Name} data-field="Name" class="slds-m-bottom_x-small"></lightning-input>
<lightning-input label="Phone" value={phone} data-field="Phone" class="slds-m-bottom_x-small"></lightning-input>
<lightning-button label="Update Account With Calling Apex" variant="brand" onclick={updateAccount}></lightning-utton>
</template>
</div>
</lightning-card>
</template>
import { LightningElement, track, wire, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { refreshApex } from '@salesforce/apex';
import getSingleAccount from '@salesforce/apex/AccountController.getSingleAccount';
import updateSingleAccount from '@salesforce/apex/AccountController.updateSingleAccount';
export default class Sample5 extends LightningElement {
@api recordId;
@track error;
// 1.0
@wire(getSingleAccount, { id: '$recordId'})
account;
// 2.0
get phone() {
return this.account.data.Phone;
}
updateAccount() {
const allValid = [...this.template.querySelectorAll('lightning-input')]
.reduce((validSoFar, inputFields) => {
inputFields.reportValidity();
return validSoFar && inputFields.checkValidity();
}, true);
if (allValid) {
let new_account = {};
new_account.Id = this.recordId;
new_account.Name = this.template.querySelector("[data-field='Name']").value;
new_account.Phone = this.template.querySelector("[data-field='Phone']").value;
// 3.0
updateSingleAccount({ account: new_account })
.then((result) => {
console.log(result);
// 4.0
refreshApex(this.account)
.then(() => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Account created',
variant: 'success'
})
);
});
})
.catch((error2) => {
console.log(error2);
this.dispatchEvent(
new ShowToastEvent({
title: 'Error creating record',
message: 'Error',
variant: 'error'
})
);
});
}else {
this.dispatchEvent(
new ShowToastEvent({
title: 'Something is wrong',
message: 'Check your input and try again.',
variant: 'error'
})
);
}
}
}
※1.0:wireサービスでApexのメソッドを指定する。importを忘れずに。
※2.0:Apexのメソッドの戻り値の取得のやり方。fieldsとかvalueとかいらない点に注意。
※3.0:Apexのメソッドを呼び出し。importを忘れずに
※4.0:データを更新した場合、最新の情報を再取得する必要あり。Apexメソッド(ここではgetSingleAccount)を使っている場合は、refreshApexを呼び出して再取得する必要あり。getRecordを使っている場合は勝手に取得されるので不要。
補足
wireサービスの引数に動的な変数を渡すときは$を付ける
変数に$を付けてwireサービスの引数に指定すると、変数の値が変わった場合にwireサービスが最新の値を取得しなおしてくれる。
基本的には、下記の考えで$を付けたらいい。
- 引数が動的な値の場合$を付ける
- 引数が固定値(フィールド値とか)の場合$はつけない
Apexメソッドでデータを取得していて再取得したいときはrefreshApexを使う
Apexメソッドを呼び出してデータを取得しているLWCでデータの更新をした場合、refreshApexを呼び出して最新の値を取り直すこと。
やり方がいろいろあるのはわかったけど、どれを使えばいいのか
以下の順番に検討する
- 基本的にはlightning-record-*-formを使う(参照)
- lightning-record-*-formで実現できない複雑なフォームを使う場合はLightningデータサービスワイヤアダプタ(getRecordとか)を使う
- 上記で対応できない特殊なケース(※)でApexを使う
※Apexを使う特殊なケース
- getRecordとかで取得できない特殊なオブジェクト (ToDo や行動など) を取り扱う場合
- getRecordで取り扱えない大きなデータ(200件以上のレコードとか)を取り扱う場合
- 複数のオブジェクトを更新してトランザクション(ロールバック)を使いたい場合
getRecordとかupdateRecord以外にもJavaScript APIってあるの?
参考
https://developer.salesforce.com/docs/component-library/documentation/ja-jp/lwc/lwc.data_guidelines
https://developer.salesforce.com/docs/component-library/documentation/ja-jp/lwc/lwc.data_wire_service_about