14
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Lightning Web Componentにおけるデータ参照/更新/登録

Posted at

参照

Lightning web componentにおけるデータ取得/更新/登録のやり方を記載する。

内容

getRecordを使ったデータ取得

html
<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に値が入っていないため。

javascript
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を使ったデータ取得(例外処理付き)

html
<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>
javascript
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を使ったデータ更新

html
<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>
javascript
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を使ったデータ登録

html
<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>
javascript
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メソッドを呼び出してデータ取得/更新

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を付ける(必須)

html
<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>
javascript
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/en/lwc/lwc.reference_lightning_ui_api_record

参考

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

14
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?