b2c-forms
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseForms Skill
表单开发技能
This skill guides you through creating forms with validation in Salesforce B2C Commerce using the SFRA patterns.
本技能将指导你使用SFRA模式在Salesforce B2C Commerce中创建带验证功能的表单。
Overview
概述
B2C Commerce forms consist of three parts:
- Form Definition - XML file defining fields, validation, and actions
- Controller Logic - Server-side form handling and processing
- Template - ISML template rendering the HTML form
B2C Commerce表单由三部分组成:
- 表单定义 - 定义字段、验证规则和操作的XML文件
- 控制器逻辑 - 服务器端表单处理与业务逻辑
- 模板 - 渲染HTML表单的ISML模板
File Location
文件位置
Forms are defined in the cartridge's directory:
forms/my-cartridge
/cartridge
/forms
/default # Default locale
profile.xml
contact.xml
/de_DE # German-specific (optional)
address.xml表单定义在 cartridge 的 目录下:
forms/my-cartridge
/cartridge
/forms
/default # 默认语言环境
profile.xml
contact.xml
/de_DE # 德语专属(可选)
address.xmlForm Definition (XML)
表单定义(XML)
Basic Structure
基本结构
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>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>Field Types
字段类型
| Type | Description | HTML Input |
|---|---|---|
| Text input | |
| Whole number | |
| Decimal number | |
| Checkbox | |
| Date value | |
| 类型 | 描述 | HTML输入控件 |
|---|---|---|
| 文本输入 | |
| 整数 | |
| 小数 | |
| 复选框 | |
| 日期值 | |
Key Field Attributes
关键字段属性
| Attribute | Purpose | Example |
|---|---|---|
| Field identifier (required) | |
| Resource key for label | |
| Data type (required) | |
| Required field | |
| Max string length | |
| Min string length | |
| Validation pattern | |
| 属性 | 用途 | 示例 |
|---|---|---|
| 字段标识符(必填) | |
| 标签对应的资源键 | |
| 数据类型(必填) | |
| 是否为必填字段 | |
| 字符串最大长度 | |
| 字符串最小长度 | |
| 验证正则表达式 | |
Validation Error Messages
验证错误消息
| Attribute | When Triggered |
|---|---|
| Mandatory field is empty |
| Value doesn't match regexp or type |
| Value outside min/max range |
| General validation failure |
See Form XML Reference for complete field attributes, groups, lists, and validation patterns.
| 属性 | 触发场景 |
|---|---|
| 必填字段为空时 |
| 值不符合正则表达式或数据类型时 |
| 值超出最小/最大范围时 |
| 通用验证失败时 |
完整的字段属性、分组、列表及验证规则请参考 Form XML Reference。
Controller Logic (SFRA)
控制器逻辑(SFRA)
Rendering a Form
渲染表单
javascript
'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();javascript
'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(); // 重置之前的值
res.render('account/profile', {
profileForm: form
});
next();
}
);
module.exports = server.exports();Processing Form Submission
处理表单提交
javascript
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;
}javascript
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();
}
// 访问表单值
var email = form.email.value;
var firstName = form.firstName.value;
// 处理并保存数据
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;
}Prepopulating Forms
预填充表单
javascript
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();
});javascript
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();
});Template (ISML)
模板(ISML)
Basic Form Template
基础表单模板
html
<form action="${pdict.actionUrl}" method="POST" name="profile-form"
class="form-horizontal" data-action="${URLUtils.url('Profile-Submit')}">
<!-- CSRF Token -->
<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>html
<form action="${pdict.actionUrl}" method="POST" name="profile-form"
class="form-horizontal" data-action="${URLUtils.url('Profile-Submit')}">
<!-- CSRF Token -->
<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>Localization
本地化
Form labels and errors use resource bundles:
forms.properties:
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=Submitforms_de_DE.properties:
properties
form.email.label=E-Mail-Adresse
form.email.required=E-Mail ist erforderlich表单标签和错误消息使用资源包:
forms.properties:
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=Submitforms_de_DE.properties:
properties
form.email.label=E-Mail-Adresse
form.email.required=E-Mail ist erforderlichBest Practices
最佳实践
- Always use CSRF protection for form submissions
- Clear forms before displaying to reset state
- Use resource keys for labels and errors (localization)
- Validate server-side even with client-side validation
- Use for database operations
route:BeforeComplete - Return JSON for AJAX form submissions
- 始终为表单提交启用CSRF保护
- 显示表单前先清空以重置状态
- 使用资源键来定义标签和错误消息(支持本地化)
- 即使有客户端验证,也要进行服务器端验证
- **使用**执行数据库操作
route:BeforeComplete - AJAX表单提交返回JSON格式数据
Detailed Reference
详细参考
For comprehensive form patterns:
- Form XML Reference - Complete XML schema, validation patterns, and examples
如需了解完整的表单模式:
- Form XML Reference - 完整的XML schema、验证规则及示例