b2c-forms について
Salesforce B2C Commerce 開発者向けのフォームスキルです。SFRA のパターンに沿った XML 定義、コントローラー処理、ISML テンプレート、ローカライズ、送信時の保護を扱います。
主な機能
- 01XML での項目と検証ルールの定義
- 02SFRA コントローラーでのフォーム処理
- 03ISML テンプレートによるフォーム表示
- 04ラベルとエラーメッセージのローカライズ
- 05CSRF 対策とサーバー側検証
活用シーン
- 01プロフィールや問い合わせフォームの作成
- 02送信されたフォームデータの検証
- 03AJAX フォーム送信の実装
- 04フォームのラベルとエラーのローカライズ
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ salesforcecommercecloud/ b2c-developer-tooling --skill b2c-formsインストール後、Claude Code で/b2c-formsを入力して読み込まれたか確認します。
同梱ファイルevals/trigger-evals.jsonreferences/FORM-XML.md
詳細
フォームスキル
このスキルでは、SFRA のパターンを使用して Salesforce B2C Commerce でバリデーション付きフォームを作成する方法を説明します。
概要
B2C Commerce のフォームは、次の 3 つの要素で構成されます。
- フォーム定義 - フィールド、バリデーション、アクションを定義する XML ファイル
- コントローラーロジック - サーバーサイドでのフォーム処理とデータ処理
- テンプレート - HTML フォームを描画する ISML テンプレート
ファイルの場所
フォームは、カートリッジの forms ディレクトリで定義します。
/my-cartridge
/cartridge
/forms
/default # Default locale
profile.xml
contact.xml
/de_DE # German-specific (optional)
address.xml
フォーム定義(XML)
基本構造
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.demandware.com/xml/form/2008-04-19">
<field formid="email" label="form.email.label" type="string"
mandatory="true" max-length="50"
regexp="^[\w.%+-]+@[\w.-]+\.\w{2,6}$"
parse-error="form.email.invalid"/>
<field formid="password" label="form.password.label" type="string"
mandatory="true" min-length="8" max-length="255"
missing-error="form.password.required"/>
<field formid="rememberMe" label="form.remember.label" type="boolean"/>
<action formid="submit" valid-form="true"/>
<action formid="cancel" valid-form="false"/>
</form>
フィールドの種類
| 種類 | 説明 | HTML 入力 |
|---|---|---|
string | テキスト入力 | <input type="text"> |
integer | 整数 | <input type="number"> |
number | 小数 | <input type="number"> |
boolean | チェックボックス | <input type="checkbox"> |
date | 日付の値 | <input type="date"> |
主なフィールド属性
| 属性 | 目的 | 例 |
|---|---|---|
formid | フィールド識別子(必須) | formid="email" |
label | ラベルのリソースキー | label="form.email.label" |
type | データ型(必須) | type="string" |
mandatory | 必須フィールド | mandatory="true" |
max-length | 文字列の最大長 | max-length="100" |
min-length | 文字列の最小長 | min-length="8" |
regexp | バリデーションパターン | regexp="^\d{5}$" |
バリデーションエラーメッセージ
| 属性 | 発生する条件 |
|---|---|
missing-error | 必須フィールドが空の場合 |
parse-error | 値が正規表現または型に一致しない場合 |
range-error | 値が最小値または最大値の範囲外の場合 |
value-error | 一般的なバリデーションエラーが発生した場合 |
フィールド属性、グループ、リスト、バリデーションパターンの全項目については、フォーム XML リファレンスを参照してください。
コントローラーロジック(SFRA)
フォームの描画
'use strict';
var server = require('server');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');
server.get('Show',
csrfProtection.generateToken,
function (req, res, next) {
var form = server.forms.getForm('profile');
form.clear(); // Reset previous values
res.render('account/profile', {
profileForm: form
});
next();
}
);
module.exports = server.exports();
フォーム送信の処理
server.post('Submit',
server.middleware.https,
csrfProtection.validateAjaxRequest,
function (req, res, next) {
var form = server.forms.getForm('profile');
if (!form.valid) {
res.json({
success: false,
fields: getFormErrors(form)
});
return next();
}
// Access form values
var email = form.email.value;
var firstName = form.firstName.value;
// Process and save data
this.on('route:BeforeComplete', function () {
var Transaction = require('dw/system/Transaction');
Transaction.wrap(function () {
customer.profile.email = email;
customer.profile.firstName = firstName;
});
});
res.json({ success: true });
next();
}
);
function getFormErrors(form) {
var errors = {};
Object.keys(form).forEach(function (key) {
if (form[key] && form[key].error) {
errors[key] = form[key].error;
}
});
return errors;
}
フォームへの事前入力
server.get('Edit', function (req, res, next) {
var form = server.forms.getForm('profile');
form.clear();
var profile = req.currentCustomer.profile;
form.firstName.value = profile.firstName;
form.lastName.value = profile.lastName;
form.email.value = profile.email;
res.render('account/editProfile', { profileForm: form });
next();
});
テンプレート(ISML)
基本的なフォームテンプレート
<form action="${pdict.actionUrl}" method="POST" name="profile-form"
class="form-horizontal" data-action="${URLUtils.url('Profile-Submit')}">
<input type="hidden" name="${pdict.csrf.tokenName}" value="${pdict.csrf.token}"/>
<div class="form-group ${pdict.profileForm.email.mandatory ? 'required' : ''}">
<label for="email" class="form-control-label">
${Resource.msg('form.email.label', 'forms', null)}
</label>
<input type="email"
id="email"
name="email"
class="form-control ${pdict.profileForm.email.error ? 'is-invalid' : ''}"
value="${pdict.profileForm.email.value || ''}"
<isif condition="${pdict.profileForm.email.mandatory}">required</isif>
maxlength="${pdict.profileForm.email.maxLength || 50}"/>
<isif condition="${pdict.profileForm.email.error}">
<div class="invalid-feedback">${pdict.profileForm.email.error}</div>
</isif>
</div>
<button type="submit" class="btn btn-primary">
${Resource.msg('button.submit', 'forms', null)}
</button>
</form>
ローカライズ
フォームのラベルとエラーにはリソースバンドルを使用します。
forms.properties:
form.email.label=Email Address
form.email.required=Email is required
form.email.invalid=Please enter a valid email address
form.password.label=Password
button.submit=Submit
forms_de_DE.properties:
form.email.label=E-Mail-Adresse
form.email.required=E-Mail ist erforderlich
ベストプラクティス
- フォーム送信には必ず CSRF 保護を使用してください
- 表示前にフォームをクリアして状態をリセットしてください
- ラベルとエラーにはリソースキーを使用してください(ローカライズ)
- クライアントサイドのバリデーションがあっても、サーバーサイドで検証してください
- データベース操作には**
route:BeforeCompleteを使用**してください - AJAX フォーム送信ではJSON を返してください
詳細リファレンス
フォームのパターンの詳細については、次を参照してください。
- フォーム XML リファレンス - XML スキーマ、バリデーションパターン、例の全項目
コメント
Markdown 対応