🙆♀️
【LWC】Datatableでインライン編集機能を実装する(バリデーション付き)
はじめに
LWC(Lightning Web Component)でDatatableのインライン編集機能を実装しました。バリデーションチェックもしてくれます。
完成形
実装方法
コード
HTML
datatable.html
<template>
<lightning-card title="取引先一覧">
<lightning-button
lwc:if={draftAccounts.length}
label="キャンセル"
slot="actions"
onclick={handleCancel}
class="slds-var-m-right_x-small">
</lightning-button>
<lightning-button
label="保存"
slot="actions"
variant="brand"
onclick={handleSave}
disabled={isNotEdited}>
</lightning-button>
<div class="slds-card__body slds-card__body_inner">
<lightning-datatable
key-field="Id"
data={accounts}
columns={columns}
draft-values={draftAccounts}
errors={tableError}
oncellchange={handleCellChange}
hide-checkbox-column
suppress-bottom-bar>
</lightning-datatable>
</div>
</lightning-card>
</template>
JavaScript
datatable.js
import { LightningElement } from 'lwc';
import fetchAccounts from '@salesforce/apex/DatatableController.fetchAccounts';
import updateAccounts from '@salesforce/apex/DatatableController.updateAccounts';
import Toast from 'lightning/toast';
const columns = [
{ label: '取引先名', fieldName: 'Name', editable: true },
{ label: '訪問回数', fieldName: 'NumberOfVisits__c', type: 'number', editable: true },
{ label: 'Webサイト', fieldName: 'Website', type: 'url', editable: true }
];
export default class Datatable extends LightningElement {
accounts = []; // Datatableに表示する取引先
draftAccounts = []; // インライン編集で変更された取引先
tableError = {}; // インライン編集後のエラー
columns = columns;
get isNotEdited() {
return !this.draftAccounts.length;
}
connectedCallback() {
this.fetchTableData();
}
showSuccessToast(label) {
Toast.show({
label: label,
mode: 'dismissible',
variant: 'success'
});
}
showErrorToast(message) {
Toast.show({
label: 'エラーが発生しました。',
message: message,
mode: 'dismissible',
variant: 'error'
});
}
async fetchTableData() {
try {
this.accounts = await fetchAccounts();
} catch {
this.showErrorToast('取引先を正しく取得できませんでした。');
}
}
handleCellChange(event) {
// event.target.draftValuesに変更後の値が格納されている
this.draftAccounts = [...event.target.draftValues];
}
async prepareValidationError() {
const rowsError = {};
// 行でループ
for (const account of this.draftAccounts) {
let errorMessages = [];
let errorFieldNames = new Set();
// 列でループ
for (const column of Object.keys(account)) {
switch (column) {
case 'Name':
if (!account[column]) {
errorMessages.push('取引先名の入力は必須です。');
errorFieldNames.add(column);
}
break;
case 'NumberOfVisits__c':
if (Number(account[column]) < 0) {
errorMessages.push('訪問回数は正の値で入力してください。');
errorFieldNames.add(column);
}
if (!Number.isInteger(Number(account[column]))) {
errorMessages.push('訪問回数は整数で入力してください。');
errorFieldNames.add(column);
}
break;
case 'Website':
if (!account[column].startsWith('https://')) {
errorMessages.push('Webサイトは「https://」で始まる文字列を入力してください。');
errorFieldNames.add(column);
}
break;
}
}
if (errorMessages.length) {
rowsError[account.Id] = {
title: errorMessages.length + '件のエラーがあります。',
messages: errorMessages,
fieldNames: [...errorFieldNames]
}
}
}
return rowsError;
}
async handleSave() {
try {
const rowsError = await this.prepareValidationError();
if (Object.keys(rowsError).length) {
// バリデーションエラー出力
this.tableError = { rows: rowsError };
} else {
this.tableError = {};
await updateAccounts({ records: this.draftAccounts });
this.draftAccounts = [];
this.showSuccessToast('取引先が更新されました。');
}
} catch(error) {
console.error(error);
this.showErrorToast('取引先を正しく更新できませんでした。');
} finally {
await this.fetchTableData();
}
}
handleCancel() {
this.tableError = {};
this.draftAccounts = [];
}
}
Apex
DatatableController.cls
public with sharing class DatatableController {
@AuraEnabled
public static List<Account> fetchAccounts() {
// 取引先レコードを、新しく作成された順に3件取得
return [
SELECT Name, NumberOfVisits__c, Website
FROM Account
ORDER BY CreatedDate DESC
LIMIT 3
];
}
@AuraEnabled
public static void updateAccounts(List<Accounts> records) {
update records;
}
}
解説
<lightning-datatable
key-field="Id"
data={data}
columns={columns}
draft-values={draftValues}
errors={errors}
oncellchange={handleCellChange}>
</lightning-datatable>
インライン編集を有効化する
インライン編集したい列にedatable: true
を設定し、columns
に受け渡します。
変更後の値を上書きする
oncellchange
に指定したメソッドは、あるセルのインライン編集が終わり、別の場所へフォーカスしたときに実行されます。
引数のevent
から、event.target.draftValues
とたどることでインライン編集後のデータが取得できます。this.template.querySelector('lightning-datatable').draftValues
とHTML要素を直接指定して取得することも可能です。
このとき、その変数の値は
[
{
Id: 'xxxxxxxxxxxxxxx',
Name: 'xxx株式会社_new',
},
{
Id: 'yyyyyyyyyyyyyyy',
Website: 'https://yyy-new-example.com'
}
]
のように、Id(key-field
で指定された値)と変更された列の値で配列として構成されています。(変更されていない行は含まれません。)
これをそのままdraft-values
に渡してあげることで、変更後の値がセルに反映され、色が変わるようになります。
バリデーションエラーメッセージを表示する
{
rows: {
xxxxxxxxxxxxxxx: {
title: '2件のエラーがあります。'
messages: [
'○○は××で入力してください。',
'hogehogeの入力は必須です。'
],
fieldNames: ['Name', 'NumberOfVisits__c']
},
yyyyyyyyyyyyyyy: {
title: '1件のエラーがあります。'
messages: [
'○○は××で入力してください。',
],
fieldNames: ['Name']
}
}
}
上記のような変数を用意してerrors
に渡せば、特定の行(Id)の特定の列(fieldNames)で指定されたセルに赤枠をつけ、Datatable左端にエラーを表示することができます。
Datatable左端以外にもDatatable全体としてのエラーを表示できるらしいですが、ここでは割愛します。
参考
終わりに
インライン編集まわりは意外とよく使う?にもかかわらず、融通が利かないところが多々あるので、また何か見つけたら更新したいと思います。
Discussion