※ これから記載する事項は、私が所属する会社とは一切関係のない事柄です。
今回は v59 (Winter ‘24) でチェックアウトのコンポーネントの作り方が少し変わったので紹介したいと思います。(ヘルプ)
以前と何が変わったのか?
今回作ってみるコンポーネント
今回は二つのコンポーネントを作成したいと思います。
1. 入力ボックスからカートオブジェクト(WebCart)のカスタムフィールド(SampleText__c)を編集するコンポーネント(以下、Sample Custom Field)
WebCart オブジェクトに 「SampleText__c」 というフィールドを追加して、メールアドレスを値として更新するコンポーネントです。入力した値がメールアドレスの形式じゃないとエラーが発生します。
入力ボックスを利用した際のイベント処理と Apex コントローラを通して値を更新するサンプルです。
2. チェックアウトの Data Provider の値を更新して購入完了ボタンの表示・非表示を切り替えるコンポーネント (以下、Sample Hide Place Order)
チェックボックスを用意しチェックをすると右上の「Place Order」ボタンが非表示になります。これは実際ではあまりないですが、これを応用して入力状況に応じてボタンを押せないようにするような UI を作成することができます。
またこれは Data Provider の値の変更の仕方のサンプルであり、これを応用すれば dispatchUpdateAsync()
を利用して配送先やメールアドレスなどの標準データの変更も行うことが可能です。
更新リクエストできる値はコード内の CheckoutFormRequest をご覧ください。
ソースコード
1. Sample Custom Field
<template>
<template if:false={isSummarized}>
<lightning-input
type="email"
label="テスト"
onchange={handleInput}
value={value}
></lightning-input>
</template>
<template if:true={isSummarized}>
<label>テスト:{value}</label>
</template>
</template>
import { CheckoutComponentBase } from 'commerce/checkoutApi';
import { api, wire } from 'lwc';
import { CartSummaryAdapter } from 'commerce/cartApi';
import updateValue from '@salesforce/apex/SampleCustomFieldContoroller.updateValue';
import getValue from '@salesforce/apex/SampleCustomFieldContoroller.getValue';
export default class SampleCustomFieldInput extends CheckoutComponentBase {
isSummarized = true
value
_cartId
async _initialize() {
try {
const data = await getValue({ cartId: this._cartId });
this.value = data.SampleText__c;
this.dispatchRequestAspect({ summarizable: false, uneditable: false });
} catch (e) {
console.error(e);
}
}
@wire(CartSummaryAdapter)
async cart({ data, error }) {
if (data) {
this._cartId = data.cartId;
this._initialize();
} else if (error) {
console.error(error);
}
}
handleInput(event) {
this.value = event.detail.value;
this.checkValidity();
}
checkValidity() {
const el = this.template.querySelector('lightning-input');
el.reportValidity();
return el.checkValidity();
}
// インターフェースの実装
setAspect(newAspect) {
this.isSummarized = newAspect.summary;
}
// インターフェースの実装
async stageAction(checkoutStage) {
switch (checkoutStage) {
case 'REPORT_VALIDITY_SAVE':
// セクション完了時に更新
if (!this.checkValidity()) {
return false
}
try {
await updateValue({ cartId: this._cartId, value: this.value });
return true
} catch (e) {
this.dispatchUpdateErrorAsync({
groupId: 'TestError',
type: '/commerce/errors/checkout-failure',
exception: 'Test error occures.',
});
return false
}
default:
return true;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Sample Custom Field</masterLabel>
<targets>
<target>lightningCommunity__Page</target>
<target>lightningCommunity__Default</target>
</targets>
</LightningComponentBundle>
public with sharing class SampleCustomFieldContoroller {
@AuraEnabled(cacheable=false)
public static WebCart getValue(String cartId) {
return [SELECT Id, SampleText__c FROM WebCart WHERE Id = :cartId LIMIT 1];
}
@AuraEnabled(cacheable=false)
public static WebCart updateValue(String cartId, String value) {
WebCart cart = [
SELECT Id, SampleText__c
FROM WebCart
WHERE Id = :cartId
LIMIT 1
];
cart.SampleText__c = value;
update cart;
return cart;
}
}
2. Sample Hide Place Order
<template>
<div>
<input
type="checkbox"
id="testhidecheckoutbtn"
checked={checked}
onchange={handleChange}
></input>
<label for="testhidecheckoutbtn">
<lightning-formatted-rich-text
value="チェックアウトボタンを隠す"
></lightning-formatted-rich-text>
</label>
</div>
</template>
import { CheckoutComponentBase } from 'commerce/checkoutApi';
import { api, track } from 'lwc';
export default class SampleHidePlaceOrderBtn extends CheckoutComponentBase {
@track
_checkoutDetails
@api
get checkoutDetails() {
return this._checkoutDetails;
}
set checkoutDetails(value) {
this._checkoutDetails = value;
}
get checked() {
return this._checkoutDetails.display.hidePlaceOrderButton
}
async handleChange(event) {
// 入力時に更新
await this.dispatchUpdateAsync({
display: {
hidePlaceOrderButton: event.target.checked,
},
});
this.dispatchCommit();
}
// インターフェースの実装
async stageAction(checkoutStage) {
switch (checkoutStage) {
case 'CHECK_VALIDITY_UPDATE':
// dispatchCommit() が呼び出された際に「CHECK_VALIDITY_UPDATE」が渡される
return true;
case 'REPORT_VALIDITY_SAVE':
// dispatchCommit() が呼び出された際に「REPORT_VALIDITY_SAVE」が渡される
return true;
default:
return true;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Sample Hide Place Order</masterLabel>
<targets>
<target>lightningCommunity__Page</target>
<target>lightningCommunity__Default</target>
</targets>
<targetConfigs>
<targetConfig targets="lightningCommunity__Default">
<property name="checkoutDetails" label="Checkout Binding Expression" type="String"
default="{!Checkout.Details}" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
ソースコードのポイント
共通
- コンポーネントは CheckoutComponentBase を継承します。CheckoutComponentBase は CheckoutComponent インターフェースを実装しています。
- 主に CheckoutComponent インターフェースの
stageAction()
とsetAspect()
関数を実装します。
Sample Custom Field
-
カートの情報(今回はカート ID)は CartSummaryAdapter を
@wire
で呼び出して取得しています。 -
setAspect()
チェックアウトが 1 ページレイアウトではなく、アコーディオンレイアウト(ヘルプ)の時に入力ボックスではなく、値を表示する際に利用します。(下記画像) -
入力ボックスのバリデーション(
checkValidity()
)はリアルタイムで実行されます。 -
Apex を通した保存(
updateValue()
)は「Place Order」ボタンを押したとき、またはアコーディオンレイアウトの場合はアクションボタンを押したときに発生するREPORT_VALIDITY_SAVE
アクション時に実行されます。(アクションの発生タイミング) -
SampleText__c
フィールドを取得・更新する Apex を作成して、コンポーネント内でコールしています。
Sample Hide Place Order
- コンポーネントへのデフォルトプロパティとして設定している
{!Checkout.Details}
という変数でチェックアウトの情報を取得しています。このオブジェクトは@track
で変更をウォッチしています。 -
dispatchUpdateAsync()
で Data Provider のhidePlaceOrderButton
を編集しています。 - 今回は
hidePlaceOrderButton
を編集しただけですが、CheckoutFormRequest 内のdefaultDeliveryGroup
などを修正すれば配送先などを修正できます。 - 今回は利用していませんが dispatchCommit を呼び出すと
CHECK_VALIDITY_UPDATE
とREPORT_VALIDITY_SAVE
アクションが発生します。両方で 「true」 を返せば配送先やメールアドレスなどが永続化されます。(フロー)