mcpField
B
b2c-forms

b2c-forms

github.com/salesforcecommercecloudDevelopment5785 installsUpdated today
Repository

About b2c-forms

A Salesforce B2C Commerce skill for developers building and validating forms with SFRA patterns. It covers XML definitions, server-side controller handling, ISML templates, localization, and submission safeguards.

Key features

  1. 01Define fields and validation in XML
  2. 02Handle forms in SFRA controllers
  3. 03Render forms with ISML templates
  4. 04Localize labels and error messages
  5. 05Apply CSRF and server-side validation

Use cases

  1. 01Create profile or contact forms
  2. 02Validate submitted form data
  3. 03Build AJAX form submissions
  4. 04Support localized form labels and errors

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-forms

After installing, type/b2c-formsin Claude Code to confirm it loaded.

Bundled filesevals/trigger-evals.jsonreferences/FORM-XML.md

Details

From the project README

Forms Skill

This skill guides you through creating forms with validation in Salesforce B2C Commerce using the SFRA patterns.

Overview

B2C Commerce forms consist of three parts:

  1. Form Definition - XML file defining fields, validation, and actions
  2. Controller Logic - Server-side form handling and processing
  3. Template - ISML template rendering the HTML form

File Location

Forms are defined in the cartridge's forms directory:

/my-cartridge
    /cartridge
        /forms
            /default              # Default locale
                profile.xml
                contact.xml
            /de_DE               # German-specific (optional)
                address.xml

Form Definition (XML)

Basic Structure
<?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
TypeDescriptionHTML Input
stringText input<input type="text">
integerWhole number<input type="number">
numberDecimal number<input type="number">
booleanCheckbox<input type="checkbox">
dateDate value<input type="date">
Key Field Attributes
AttributePurposeExample
formidField identifier (required)formid="email"
labelResource key for labellabel="form.email.label"
typeData type (required)type="string"
mandatoryRequired fieldmandatory="true"
max-lengthMax string lengthmax-length="100"
min-lengthMin string lengthmin-length="8"
regexpValidation patternregexp="^\d{5}$"
Validation Error Messages
AttributeWhen Triggered
missing-errorMandatory field is empty
parse-errorValue doesn't match regexp or type
range-errorValue outside min/max range
value-errorGeneral validation failure

See Form XML Reference for complete field attributes, groups, lists, and validation patterns.

Controller Logic (SFRA)

Rendering a Form
'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();
Processing Form Submission
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;
}
Prepopulating Forms
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)

Basic Form Template
<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:

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

Best Practices

  1. Always use CSRF protection for form submissions
  2. Clear forms before displaying to reset state
  3. Use resource keys for labels and errors (localization)
  4. Validate server-side even with client-side validation
  5. Use route:BeforeComplete for database operations
  6. Return JSON for AJAX form submissions

Detailed Reference

For comprehensive form patterns:

Comments

Markdown supported