Bundles

The heart of Expression, where everything comes together. Learn about Bundle concepts and how to build anything you need on the framework.

Bundles are the top level container for your code assets, encapsulating Elements, Datasources, Serverside Scripts and Post Handlers. Typically you will edit your code through the Bundle Editor in the XPR Backend.

The Bundle Editor uses Ace as its code editor, you can find more information about usage here.

DocSuite Bundles

The primary functionality of Expression scripting, rendering, form processing and bundle interaction are illustrated through a series of Bundles in the Marketplace which serve as practical examples of the techniques described in this document. Checking them out is reccomended if you are new to the platform.

Bundle Paths


Every entity within a bundle is referenced elsewhere in the system by their bundlePath which follows the pattern of {bundleName}/{asset}. The type of the asset does not need to be specified because lookups are contextual, i.e. in Element View Templates, the {{{XprElement}}} helper accepts a bundlePath argument, so it will look inside the specified bundle for an element. The one exception to this, is for BundleFiles which are text files which can be served on the frontend of the website, e.g.:

{{{XprBundleFile bundlePath="{bundleName}/{fileType}/{asset}"}}}

<link rel='stylesheet' href='{{{XprBundleFile bundlePath="ArticleRenderer/css/Styles"}}}'/>

If accessing an entity within a bundle, you do not need to specify the {bundleName} portion of the path. Path resolution rules are as follows:

  • First, look for entitiy inside bundle
  • Second, look to imports (See Path Remapping)
  • Third, look for a full path match

Path Remapping

Path remapping is a powerful way to build modular Bundles. For example, you could have one bundle whose primary job is fetching, sorting, and relating content, and another bundle which contains the Elements to render the fetched content. This is accomplished by adding an imports section to the Bundle configuration (the JSON configuration file pointed to by the main Bundle properties panel)

{
    ... other configuration options...

    "imports" : {
        "LocalName" : "ForeignName"
    }
}

Bundle Repos & Branching

Each Bundle is stored both locally on the Expression instance, and remotely on a Git repository. Bundles utilize a two branch model. A typical workflow is as follows:

  • Developer does their work on the development branch.
  • When a logical 'chunk' of work has been completed, while on the development branch, the Developer will click on the commit -m in the Bundle editor on the main Bundle Panel (activated by clicking on the Bundle name header in the entity tree on the left-hand side)
  • Expression prompts for a commit message, which is used to identify the chunk of work which was just completed.

At this point, the code will not be live for non-developers. Assuming an organizational structure where a code 'gatekeeper', i.e. manager or team lead, will be reviewing changes, the following steps will be performed. Note: on small teams or sites managed by individuals, there is no reason why the Gatekeeper and Developer would not be the same user.`

  • Gatekeeper goes to their user-profile or uses the top-right user dropdown to change the Active Frontend Branch to 'development'
  • Changes are previewed on the website frontend to ensure they are functioning as expected
  • Navigating to the Bundle in the backend, the git merge development is pushed
  • The code which has changed relative to the master branch will be shown in a diff display. Upon accepting, the code lands on master and is now live.

Reverting Changes

Every commit and merge operation is essentially a checkpoint in time for your source-code. You can always go back to your last commit (on dev) or merge (on master) operation by pushing the git reset --hard button on the main Bundle property panel. This allows you to safely make experimental changes.

Bundle Marketplace

The Bundle Marketplace is a global store of bundles which can be installed on your local Expression instance. The listing of bundles can be updated with what is available in the Marketplace by selecting the 'synchronize Bundle Marketplace' option. Uninstalled bundles will have an 'install' button next to their listing in the data explorer.

Installation creates repository specifically for the instance of this Bundle on your site. Modifications can be made on this clone of the bundle specific to your Expression instance.

As time goes on, the maintainers of the Marketplace Bundle may publish an update. When an update is published, you can select the 'Upgrade' button on the Bundle Explorer. A display will show you exactly what code changes have occurred so you can vet them prior to installing the updated bundle.

XprTrace Session


The bundle editor includes an XprTrace session slaved to your current bundle. Any test POSTs/Renders/Datasource fetches will dequeue console logs and exception information (when available) to the Trace tab.


Elements

Elements are the main building block for the frontend of your site or application. They encompass both a View Template and a list of Datasource Bindings which can optionally be routed through Serverside Scripts.

View Templates - aka Elements

View Templates are written in Handlebars. Expression differs from "stock" Handlebars in that the code is compiled and executed on the server as opposed to the client. Expression flavoured HBS also ships with a library of Helpers which enable more complex functionality.

Render Context


The Render Context is the data available for templating inside of a given View Template. It is populated in three ways:

  1. Globals (xpr.*, bundle.*)
  2. Bound Datasources
  3. Passed Context from XprElement

Rendering Elements via the Frontend

Rendering an element on the frontend is accomplished via the elementAjax route. A bundled element is rendered via the following URI scheme: /elementAjax/{bundleName}/{elementName}

The rendered element will respect the Active Frontend Branch of the logged in user (or master if no logged in user).

Builtin Helpers

{{{XprElement}}}

Argument Description
bundlePath The bundlePath which points to the Element you wish to include
context.[var]=[localVar] applies localVar to the render context of the sub element using the variable name var

The XprElement helper renders the Element pointed to by bundlePath, optionally passing context which can then be consumed by the child Element and it's attached Datasources.

Example :

<!-- Include a header Element -->  
{{{XprElement bundlePath = "Templates/Header"}}}  

<!-- Loop over an Articles collection, and pass each Article (.) to a sub Element -->
{{#each Articles}}    
    {{{XprElement bundlePath = "Renderers/ArticleRenderer" context.Article = . }}}
{{/each}}  

Sub Element Example:

<!-- the 'Article' object exists in context here because the Element was included with 'context.Article' -->
<h1> {{Article.Title}} </h1>  

{{#XprLogic}} ... {{/XprLogic}}

Argument Description
value The left hand value
tovalue The right hand value
logic The operation to perform (same abbreviations as API filters)
method='count' apply an array count to value before running the comparison

The XprLogic block-helper will render template content between the opening (#) and closing (/) tags when the condition described passes.

The keywords value and tovalue can be eliminated as long as they are passed as the first two arguments to the helper.

Examples:

<!-- check if properties are equal -->
{{#XprLogic value=.Name tovalue="Oscar" logic="eq"}}
    This object's .Name property is equal to 'Oscar'
{{/XprLogic}}  

<!-- compacted syntax -->
{{#XprLogic .Size 1000 logic="lte"}}  
    This object's .Size property is less than or equal to 1000
{{/XprLogic}}  

<!-- method='count' -->
{{#XprLogic Articles 5 logic="gt" method="count"}}  
    The collection 'Articles' has more than 5 items.
{{/XprLogic}}  

{{XprMap}}

Argument Description
(first) Key for lookup
key=value, ... Values

XprMap takes a single unnamed parameter as a key and uses that to look up from the reset of the helpers named arguments.

Example:

<!-- Outputs "Red" -->
{{XprMap "r" r="Red" g="Green" b="Blue"}}

{{XprSelect}}

Argument Description
key Key for lookup
array Array to select from

XprSelect selects an object from array based on key. Similar to XprMap but allows array to be a context variable instead of an inline map.

Example:

The first item in the array "Colors" is :{{XprSelect key=0 array=Colors}}

{{#XprSelectContext} ... {{/XprSelectContext}}

Argument Description
key Key for lookup
array Array to select from

XprSelectContext functions identically to XprSelect but functions as a block helper which imports the resolved value into the subcontext.

Example:

{{#XprSelectContext key=0 array=Articles}}
    The Title of the first item in 'Articles' is: {{.Title}}
{{/XprSelectContext}}  

{{{XprJson}}}

Argument Description
(first) Object to encode
as_attribute Escape output so as to make valid for embedding as an HTML attribute

XprJson encodes objects from the current contxt as JSON, optionally escaping them so they're appropriate for embedding as HTML attributes.

Example:

<!-- basic output -->
<script>   
var user = {{{XprJson xpr.request.user}}};
</script>    

<!-- As attribute, note wrapping "" and triple brace -->
<div id ="user-container" data-user="{{{XprJson xpr.request.user as_attribute="true"}}}">
</div>  

<script>  
  var user = $("#user-container").data("user");
</script>

{{{XprDump}}}

Argument Description
(first) Object to encode

XprDump is a debugging tool which pretty prints objects from the context as JSON, but only renders if the logged in admin user has Site Developer access.

Example:

<!-- dump the entire render context to help debug -->
{{{XprDump @root}}}  

<!-- dump the render context in our current scope -->  
{{{XprDump .}}}  

<!-- dump a specific object -->  
{{{XprDump ArticleCollection}}}  

{{XprDate}}

Argument Description
date Date to format
format Format

XprDate formats a date string according to format.

Formatting options:

Format Character Description
d Day of the month, 2 digits with leading zeros
D A textual representation of a day, three letters
j Day of the month without leading zeros
l A full textual representation of the day of the week
N ISO-8601 numeric representation of the day of the week
S English ordinal suffix for the day of the month, 2 characters
w Numeric representation of the day of the week
z The day of the year (starting from 0)
W ISO-8601 week number of year, weeks starting on Monday
F A full textual representation of a month, such as January or March
m Numeric representation of a month, with leading zeros
M A short textual representation of a month, three letters
n Numeric representation of a month, without leading zeros
t Number of days in the given month
L Whether it's a leap year
Y A full numeric representation of a year, 4 digits
y a two digit representation of a year
a Lowercase Ante meridiem and Post meridiem
A Uppercase Ante meridiem and Post meridiem
g 12-hour format of an hour without leading zeros
G 24-hour format of an hour without leading zeros
h 12-hour format of an hour with leading zeros
H 24-hour format of an hour with leading zeros
i Minutes with leading zeros
s Seconds, with leading zeros

Example:

{{XprDate date="01-01-2018" format="l, F jS Y"}}  
<!-- Outputs: Monday, January 1st 2018 -->

{{XprNumberFormat}}

Argument Description
number Number to format
(decimals) Number of decimal points
(decimal_point) Character for decimal point
(thousands_sep) Separator

XprNumberFormat formats a number.

Example:

<!-- Outputs 10,000.25 -->
{{XprNumberFormat number=10000.25 decimals=2 decimal_point="." thousands_sep=","}}

{{XprByteFormat}}

Argument Description
number Number to format
format "KB", "MB" or "GB"
(decimals) Number of decimal points
(decimal_point) Character for decimal point
(thousands_sep) Separator

XprByteFormat formats a byte count into KB, MB, or GB.

Example:

Your download will be: {{XprByteFormat number=ThisFile.Size format="KB"}} Kilobytes.

{{XprCount}}

Argument Description
(first) Collection to count

XprCount emits a count of the objects passed as the first argument.

Example:

Found: {{XprCount SearchResults}}  items.

{{{XprJsIncludesUrl}}}

XprJsIncludesUrl will emit the URL for the generated javascript, if you are using the LibraryIncludes system to manage your javascript.

Example:

<script src="{{{XprJsIncludesUrl}}}"></script>  

{{{XprCssIncludesUrl}}}

XprCssIncludesUrl will emit the URL for the generated stylesheet file if you are using the LibraryIncludes system to manage your css.

Example:

<link rel="stylesheet" type="text/css" href="{{{XprCssIncludesUrl}}}"/>

{{XprStripTags}}

Argument Description
(first) HTML data to strip

XprStripTags will remove html tags from any data. This is useful if you have some HTML data you wish to reformat, e.g. search results from an external service which supplies HTML.

Example:

{{XprStripTags WebRequest.ExternalHTMLContent}}