2
0

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 1 year has passed since last update.

Salesforce B2B / D2C Commerce のチェックアウトのカスタムコンポーネントを作成してみる

Posted at

※ これから記載する事項は、私が所属する会社とは一切関係のない事柄です。

今回は v59 (Winter ‘24) でチェックアウトのコンポーネントの作り方が少し変わったので紹介したいと思います。(ヘルプ

  • 今回は支払いコンポーネントについては取り扱いません。詳細はヘルプをご覧ください。
  • 今回は利用できる関数の一部のみを利用しているので、他の関数についてはインターフェースをご覧ください。

以前と何が変わったのか?

  • コンポーネントの階層
  • コンポーネントを作る際のインターフェース
  • コンポーネントを操作する際のフロー

今回作ってみるコンポーネント

今回は二つのコンポーネントを作成したいと思います。

sample.gif

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

sampleCustomFieldInput.html
<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>
sampleCustomFieldInput.js
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;
        }
    }

}
sampleCustomFieldInput.js-meta.xml
<?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>
SampleCustomFieldContoroller.cls
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

sampleHidePlaceOrderBtn.html
<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>
sampleHidePlaceOrderBtn.js
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;
        }
    }

}
sampleHidePlaceOrderBtn.js-meta.xml
<?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>

ソースコードのポイント

共通

Sample Custom Field

  • カートの情報(今回はカート ID)は CartSummaryAdapter@wire で呼び出して取得しています。

  • setAspect() チェックアウトが 1 ページレイアウトではなく、アコーディオンレイアウト(ヘルプ)の時に入力ボックスではなく、値を表示する際に利用します。(下記画像)

    • スクリーンショット 2023-10-30 13.50.16.png
  • 入力ボックスのバリデーション(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_UPDATEREPORT_VALIDITY_SAVE アクションが発生します。両方で 「true」 を返せば配送先やメールアドレスなどが永続化されます。(フロー
2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?