Scripting in ISML

Typed pdict from the render calls, template variables with type hints, @pdict declarations, the Script API in expressions and isscript, and the script compatibility mode

Every ${...}, every <isscript> body and every expression attribute such as condition, value or items is SFCC JavaScript with the Script API behind it. The plugin knows what the controller passed to the template, so pdict has real members, and the variables a template declares carry their types into the code that uses them

pdict has the members the controller passes to res.render. Completion lists them with their types, and the documentation beside the list says which line sets each one

Where JavaScript lives

WhereExample
${...} in text and attribute values${product.productName}
The body of <isscript>var assets = require('*/cartridge/scripts/assets');
Expression attributescondition, value, items, iterator, begin, end, step

Inside them you get the IDE's own JavaScript features: completion, Go to Declaration, parameter info and parameter name hints. The scope is the B2C Script API, the SFRA server module, ES5 and the template itself, so a browser global such as document doesn't resolve in server code. A client-side <script> block keeps the normal browser scope

These names exist in every template without a declaration:

NameType
pdictThe pipeline dictionary, typed from the render calls
Resourcedw.web.Resource
URLUtilsdw.web.URLUtils
StringUtilsdw.util.StringUtils
outdw.io.PrintWriter

pdict

pdict gets its members from three places, in this order:

@pdict lines

@pdict {Type} name description lines in a template comment declare members with a type and a description. They win over everything else, see @pdict declarations

Render calls

The members of the model passed by every controller, script and pipeline that renders this copy of the template. res.render('account/loyalty', { points: points }) adds pdict.points with the type of points

PipelineDictionary

The members of dw.system.PipelineDictionary that the platform sets for every template, such as CurrentCustomer and CurrentSession

In SFRA the model is more than the object passed to res.render. The plugin also reads res.setViewData and res.getViewData across the route's handlers, server.append and server.prepend in other cartridges and the middleware functions of the route, up to 50 functions deep. Page Designer component models count too. When a render call builds its template path at run time, its members are added as optional

A member that only some render calls set is optional. Quick documentation (F1, Ctrl+Q) on a member says which line sets it:

SectionShows
Titlepdict.points: number and the kind, pipeline dictionary member
Set by:Each assignment with its file and line, for example points: loyaltyHelpers.getPoints(customer) in app_custom/cartridge/controllers/Loyalty.js, line 5
Optional:How many render calls set it, for example "set by 2 of 3 sources"
Declared:The @pdict line, when a template header declares the member

Quick documentation on pdict itself lists its members. When some render calls aren't known or pass a computed model, it says that other members may exist

pdict flows through includes, decorators and custom tags. A custom tag's attributes arrive in its template as pdict.<attribute>, in lower case

Template variables

<isset name="basket" value="${dw.order.BasketMgr.getCurrentBasket()}" scope="page" />
${basket.totalGrossPrice}

<isset name> declares a variable with the type of its value. A later <isset> with the same name sets it again

<isloop items="${basket.productLineItems}" var="lineItem" status="loop">
    <div class="${loop.first ? 'first' : ''}">${lineItem.productName}</div>
</isloop>

var gets the element type of items, here dw.order.ProductLineItem. status is the loop status with count (from 1), index (from 0), first, last, odd and even

<isscript>
    var ProductMgr = require('dw/catalog/ProductMgr');
    var product = ProductMgr.getProduct(pdict.pid);
</isscript>
<a href="${URLUtils.url('Product-Show', 'pid', product.ID)}">${product.name}</a>

Top-level declarations in <isscript> are variables of the template. importClass, importPackage and importScript declare their names too

The innermost loop variable wins, then the nearest declaration before the use. Variables reach the templates this one includes or decorates

The type of each <isset> and <isloop> variable shows as a hint after its name, such as : dw.order.ProductLineItem. Turn the hints off with ISML variable types in Settings › Editor › Inlay Hints. Quick documentation on a variable shows where it's declared, its scope, and for a loop status the members

@pdict declarations

A template that no controller renders, such as an email or a content asset template, can declare its members in its first <iscomment>:

cartridge/templates/default/mail/loyaltyWelcome.isml
<iscomment>
    @pdict {dw.customer.Customer} customer The customer who joined
    @pdict {number} points Welcome bonus points
    @pdict {string} [title] The heading
</iscomment>
<h1>${pdict.title || Resource.msg('mail.welcome.title', 'loyalty', null)}</h1>

Brackets around the name mark an optional member. The descriptions appear in completion and quick documentation. Custom tag templates write @param lines the same way, and the plugin checks them against the <ismodule> attributes, see Custom Tags. The Declare 'pdict.x' with @pdict quick fix writes the line for you, see Inspections

Script compatibility mode

B2C Commerce runs template scripts with the compatibility mode set in Business Manager › Administration › Site Development › Code Deployment. Set the same mode in Settings › Languages & Frameworks › ISML › Script compatibility mode and the Script syntax the compatibility mode does not support inspection reports syntax your instance can't run

ModeAdds
21.2Arrow functions, for...of, generators, shorthand methods, object destructuring
22.7Shorthand properties, template literals, exponentiation, BigInt

No mode supports classes, default parameters, rest and spread, optional chaining, nullish coalescing or async functions. Not set reports only the syntax that no mode supports

On this page