HTML elements are elements which can display rich text content, such as markdown, HTML, images stored on other sites, or other resources found on the internet.
Some uses for HTML fields:
- Display attractive text using markdown syntax.
Use the same markdown symbols you may already know from Github, Reddit, or Microsoft Teams to add attractive read-only text to your configurator. No knowledge of HTML required.
- Display a dynamic, read-only table of data.
Use snap rules to populate an HTML field with well-formed <table>...</table> tags to display a read-only table of data.
-
Capture user clicks within the HTML for use in your confugurator.
If your HTML contains any item with an ID, then any click on that item can store the specific ID to a field in the configurator. In the example below, each of the rows in the table has an ID, so when they are clicked the ID appears in the configurator field above the table.
This technique is not limited to table rows. You can make individual table cells clickable, or any other HTML item.
- Display a dynamic SVG image.
Use Snap rules to display a Scalable Vector Graphics (SVG) image that changes with your data, like this pie chart.
The code for each of these examples can be found below.
HTML Properties
Name | A name which is referenced to on the left tree and rules. The name must be unique to the configurator. |
Visible | Specifies whether this element and its contents are shown to the user by default or not. This property can be changed in the visibility rules. |
Value | The HTML you want to display. |
Markdown or HTML?
Since an HTML element can display markdown, it may interpret the value as markdown commands, and not show the characters you want to display. If you see results you don't want, try one of these techniques:
- Force the text to be interpreted as HTML instead of markdown by placing a "<div>" before the text, and a "<\div>" after. For example,
<div>your content</div>
- Use a text element instead of an HTML element.
Safety First.
The HTML element does not support all HTML features, such as iframes or other HTML which cannot be supported in the context of a Epicor CPQ configurator. While we encourage innovative use of this element, be aware that...
- The use of some HTML features which work today may be disabled in the future if they cause performance, usability, or security problems. Use our preview site and next site to test platform updates safely.
- The Epicor CPQ platform works across all modern browsers (from mobile devices to tablets to desktops). Your HTML may not. You should carefully test your HTML on all of those browsers and devices as well, to prevent any surprises when you deploy your new code.
- Epicor CPQ is built on Snap, our easy-to-use visual language. We cannot support or troubleshoot questions you may have in other languages you use alongside or within Epicor CPQ, such as HTML.
Example Code
Here's the code behind each of the examples above. To use, first create an HTML element in your configurator, then paste one of the example snippets below into the value of the HTML element. You'll see the render of the HTML appear in the UI preview to the right. To see any Snap-related interactions, run the configurator.
Markdown example
####Materials Only materials that are appropriate for your environment are shown here. _See [our tips](www.github.com) for selecting materials._
HTML table example
Note how the style block helps you change the appearance of the HTML table, and allows you to control what text within the table the user can select.
<style type="text/css">.tg {border-collapse:collapse;border-spacing:0;} .tg td{padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;} .tg th{padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;} .tg {user-select: text;} /* allow the user to select text within the table (by default, this is not allowed) */ </style> <table class="tg"> <tr><th>SP #</th><th>Section</th><th>Instruction</th></tr> <tr><td>foo</td><td>bar</td><td>baz</td></tr> </table>
Capture user clicks example
-
paste this code into the HTML element:
<style>
table tr:hover {background-color: orange;}
table {border-collapse:collapse;border-spacing:0;text-align:left;}
table td {padding:5px;border-style:solid;border-width:1px;border-color:black;}
</style>
<table><tbody><tr class="highlight-row"><th>Company</th><th>Contact</th><th>Country</th></tr><tr id="alfred GENERIC ROW" class="highlight-row"><td id="alfred COMPANY CELL">Alfreds Futterkiste</td><td>Maria Anders</td><td>Germany</td></tr><tr id="centro"><td>Centro comercial Moctezuma</td><td>Francisco Chang</td><td>Mexico</td></tr><tr id="ernst"><td>Ernst Handel</td><td>Roland Mendel</td><td>Austria</td></tr><tr id="island"><td>Island Trading</td><td>Helen Bennett</td><td>UK</td></tr><tr id="magazzini"><td>Magazzini Alimentari Riuniti</td><td>Giovanni Rovelli</td><td>Italy</td></tr></tbody></table> Then create a text field in the configurator to store the ID of the item the user clicked. Here, we created a field called ClickTarget.
-
Finally, create the HTML rule to populate the field.
Select the HTML element's entry in the configurator's design tree. The entry changes color, and a plus sign appears next to it. Click the plus sign to create a click rule. In the HTML rule, write the ID of the HTML item the user clicked into the configurator field:
SVG image example
Here we render the slices array as parts of a pie chart. The "slices" array is static text in this example, but you can use Snap to dynamically create this text.
<script type = "text/javascript"> const ThisJavascriptCreatedBy = "https://jsfiddle.net/davidg707/nwqkqqv9/"; const svgEl = document.querySelector('svg'); const slices = [ { percent: 0.1, color: 'Coral' }, { percent: 0.65, color: 'CornflowerBlue' }, { percent: 0.2, color: '#00ab6b' }, ]; let cumulativePercent = 0; function getCoordinatesForPercent(percent) { const x = Math.cos(2 * Math.PI * percent); const y = Math.sin(2 * Math.PI * percent); return [x, y]; } slices.forEach(slice => { // destructuring assignment sets the two variables at once const [startX, startY] = getCoordinatesForPercent(cumulativePercent); // each slice starts where the last slice ended, so keep a cumulative percent cumulativePercent += slice.percent; const [endX, endY] = getCoordinatesForPercent(cumulativePercent); // if the slice is more than 50%, take the large arc (the long way around) const largeArcFlag = slice.percent > .5 ? 1 : 0; // create an array and join it just for code readability const pathData = [ `M ${startX} ${startY}`, // Move `A 1 1 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc `L 0 0`, // Line ].join(' '); // create a <path> and append it to the <svg> element const pathEl = document.createElementNS('http://www.w3.org/2000/svg', 'path'); pathEl.setAttribute('d', pathData); pathEl.setAttribute('fill', slice.color); svgEl.appendChild(pathEl); }); </script><div><svg viewBox="-1 -1 2 2" style="transform: rotate(-90deg)"></svg></div>
Example using Snap
The examples above are static: they do not change as your customer adjusts selections in the UI. To make the contents of the field dynamic, you can replace parts of the HTML text in the HTML field with data from your configurator, or build parts of the text dynamically. Learn more about text manipulation commands.
In the following example, you can display data from the PriceObject if the PricingWidget doesn't meet your exact needs. To use this code, first create an HTML element in your configurator called "h-DisplayComponents". Then create a new value rule. Copy the JSON below, then right-click in the Snap workspace of the rule and "paste JSON" to build the code.
[{"$type":"DeclareVariableBlock","x":55,"y":138.99998474121094,"parts":{"itemVar":{"value":"HTMLtext"},"type":{"value":"text"},"default":{"$type":"LiteralStringBlock","parts":{"text":{"value":""}}},"next":{"$type":"DeclareVariableBlock","parts":{"itemVar":{"value":"thePriceObject"},"type":{"value":"PriceObject"},"default":{"$type":"GetConfiguratorPropertyBlock","parts":{"property":{"value":"priceObject"}}},"next":{"$type":"IfBlock","parts":{"v":2,"if":{"$type":"IsNullBlock","parts":{"value":{"$type":"GetVariableBlock","parts":{"variable1":{"value":"thePriceObject"},"variable2":{"value":""}}}}},"then":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"LiteralStringBlock","parts":{"text":{"value":"<i>No price items to show.</i>"}}}}},"else":{"$type":"DeclareVariableBlock","parts":{"itemVar":{"value":"thesePriceItems"},"type":{"value":"PriceItem[]"},"default":{"$type":"GetVariableBlock","parts":{"variable1":{"value":"thePriceObject"},"variable2":{"value":"items"}}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"1. add CSS style, so table is attractive"},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"LiteralStringBlock","parts":{"text":{"value":"<style type=\"text/css\">\n.tg {border-collapse:collapse;border-spacing:0;}\n.tg td{padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}\n.tg th{padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}\n.tg {user-select: text;} /* allow the user to select text within the table (by default, this is not allowed) */\ntd:nth-child(3) { text-align: end; } \n</style>"}}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"2. Open the table"},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":"<table class=\"tg\">"}}}]}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"3. Add the first table row: table column headers"},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":" <tr>\n <th class=\"tg-abx8\">SKU</th>\n <th class=\"tg-abx8\">Description</th>\n <th class=\"tg-abx8\">Price</th>\n </tr>"}}}]}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"4. Add the remaining table rows. Each price object is a row, price object properties are cells in that row"},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"so we have a loop (to build columns) within a loop (to build rows)"},"next":{"$type":"ForEachBlock","parts":{"itemVar":{"value":"priceItem"},"arrayVar":{"$type":"GetVariableBlock","parts":{"variable1":{"value":"thesePriceItems"}}},"statement":{"$type":"CommentBlock","parts":{"comment":{"value":"4.1. Open the table row"},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":" <tr>"}}}]}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"4.2. Add the cells for that row"},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"LiteralStringBlock","parts":{"text":{"value":"<td>"}}},{"$type":"GetVariableBlock","parts":{"variable1":{"value":"priceItem"},"variable2":{"value":"sku"}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":"</td>"}}}]}}]}},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"LiteralStringBlock","parts":{"text":{"value":"<td>"}}},{"$type":"GetVariableBlock","parts":{"variable1":{"value":"priceItem"},"variable2":{"value":"description"}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":"</td>"}}}]}}]}},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"LiteralStringBlock","parts":{"text":{"value":"<td>"}}},{"$type":"FormatNumberBlock","parts":{"value":{"$type":"GetVariableBlock","parts":{"variable1":{"value":"priceItem"},"variable2":{"value":"extNetPrice"}}},"type":{"value":"currency"},"currency":{"$type":"LiteralStringBlock","parts":{"text":{"value":"EUR"}}}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":"</td>"}}}]}}]}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"4.3. Close the table row"},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":" </tr>"}}}]}}}}}}}}}}}}}}}}}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"5. Close the table"},"next":{"$type":"SetVariableBlock","parts":{"variable1":{"value":"HTMLtext"},"value":{"$type":"StringConcatBlock","parts":{"v":2,"parts":[{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},{"$type":"LiteralStringBlock","parts":{"text":{"value":"</table>"}}}]}}}}}}}}}}}}}}}}}}}}}}}}}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"6. Our HTML is built! Place it into the HTML element for display."},"next":{"$type":"SetUiObjectPropertyBlock","parts":{"obj":{"value":"692phj5tz"},"property":{"value":"value"},"value":{"$type":"GetVariableBlock","parts":{"variable1":{"value":"HTMLtext"}}},"next":{"$type":"CommentBlock","parts":{"comment":{"value":"Note that since Value rules run before Pricing rules, the data in this HTML table example will be one click behind the current pricing data."}}}}}}}}}}}}}]
Position
Container-based position properties appear for this interface element when it is placed within a container, giving you more control over it's position. If you place this UI element within a container, the following properties will appear. Remove it from the container, and these properties will disappear. Furthermore, the actual position properties themselves change, depending on that parent container's Item Layout.
- Not seeing any of the "Position" properties below for this UI element?
Place this element into a container, and then return to this element's properties list. The "Position" accordion will now appear in the properties list.
- Not seeing the right kinds of "Position" properties for this UI element?
Change the parent container's item layout to horizontal, vertical, or absolute. Then return to this UI element. The position properties available for items within that layout (marked by green checkmarks below) will appear.
Property | Available in a horizontal layout | Available in a vertical layout | Available in an absolute layout | Available in an absolute layout |
---|---|---|---|---|
Relative Size |
When using relative sizes, try setting all items within the container to the relative size of "x1" as a starting point, and then adjust the xN levels as necessary. Mixing items with "auto" and "xN" settings is allowed, but may be difficult to manage. |
|||
Alignment |
Controls how this item within a container is aligned in the cross axis of the container's layout.
|
|||
Left Top Right Bottom Width Height |
The distance between the edge of the parent container and this element. By default, each is "auto". Usually, you would set only 2 of these properties, leaving the rest to "auto", or you may distort this image or element. For example, to align an item's upper-right corner to the upper-right corner of the parent container, set "Top" to 0px, and "Right" to 0px, leaving all other distances to "auto". |
|||
Opacity |
The visibility of items or backgrounds behind this item. Use any decimal number from 0 to 1. Set to 0 to make this item completely transparent. Set to 1 to make the item completely opaque. |
|||
Order |
Specifies the order in which this item is drawn in the container. Use a whole number, such as 0 or 132. Default: 0. Larger numbers are drawn later, and appear closer to the user.
|
|||
Use Zero Padding | ![]() |
![]() |
![]() |
By default, UI elements have some padding around them for legibility on all devices, and for ease of use on touch-enabled devices. However, in some cases you may want specific UI elements to have no padding around them at all (to touch each other). This can be useful to create a portion of your UI which needs a very high information density. For example, to create a grid of fields that appear like cells in a spreadsheet. |