Expand Macros in Content List Queries

In a content list query, you can define values for properties that are calculated when a page is run, to display content that has been recently updated.

Most properties for components within sites are static. The user selects or enters a fixed string or value for one of the properties of the component, and that doesn't change regardless of when or where the page is run. However, you can define values for properties that are calculated when the page is run. This is useful for displaying content that has been recently updated in content queries. Users can enter dates such as "in the last 3 days".

You can insert a Mustache JS expansion to several properties. The values referenced in these strings are derived from a model that is executed when the page is run. An out-of-the-box model handles dates formatted for Content REST API calls. You can extend this model with additional values to meet any user requirements.

An example of the string you can enter for a property follows:

Content List component:
      Additional Query String property:
             updatedDate gt "{{#content.date}}today - 3 days{{/content.date}}"

This Mustache entry for the date will be evaluated at runtime so that the returned value changes depending on when it is run (that is, expands to updatedDate gt "2220181002060000000"). In this way, the user can build up any complex date string rather than having to enter a predefined value.

Supported Component Properties

The following properties support Mustache JS template syntax:

  • Content List

    • Additional Query String

    • For example: updatedDate gt "{{#content.date}}today - 3 days{{/content.date}}"

  • Title/Paragraph/Text

    • Rich text entered via CKEditor

    • For example: "Content REST API format for date: {{#content.date}}now{{/content.date}}"

Note:

Without a custom model for the Mustache template, the expansion in Title/Paragraph/Text isn't that useful. However, it is very useful for validating what you enter in the Additional Query String because it will be evaluated as you switch between edit and view and be immediately visible.

Supported Component Syntax

The content.date object is supported out-of-the-box. This takes in two main parameters, today and now.

The today value takes the current browser time, converts it to midnight tonight, and then converts that value to UTC time.

  • {{#content.date}}today{{/content.date}} expands to the browser value for midnight tonight, converted to the UTC value and formatted into the Content REST API date format. For example:

    2220181008065959999
  • It can then be augmented with:

    today +/-  [day | week | month | year]
  • The today value also behaves differently as you add or subtract from it. If you subtract from it, it will use the time in the morning. If you add to it, it will use the time at midnight. For example:

    • {{#content.date}}today - 1 day{{/content.date}} expands to yesterday at start of day.

    • {{#content.date}}today + 2 days{{/content.date}} expands to the day after tomorrow at midnight.

The now value takes the current browser time and converts it to UTC time without any adjustment.

  • {{#content.date}}now{{/content.date}} expands to the current browser time converted to the UTC value and formatted into the Content REST API date format.

  • now can also be augmented with hour. So you have:

    now +/-  [hour | day | week | month | year]
  • For example:
    • {{#content.date}}now + 2 hours{{/content.date}} - two hours from now converted to UTC time formatted in the Content REST API date format

    • {{#content.date}}now - 1 day{{/content.date}} - yesterday at this browser time converted to UTC time formatted to Content REST API date format

Using the Supported Component Syntax

To use the macro expansion in the additional query string, suppose you wanted to return everything in the last 3 weeks, you would enter the following:

updatedDate gt "{{#code.date}}today - 3 weeks{{/code.date}}"

Only the date is returned, so to work on the Content REST API call, quotes are added when you construct the query string as you would when entering a static value.

MustacheJS

For syntax, reference the Mustache JS template pages at mustache.github.io/mustache.5.html.

One change has been made to the Mustache instance that is run when expanding strings. Mustache provides both a text expansion that uses {{ }} and an html expansion that uses {{{ }}}. The difference between these two is that the text expansion does an HTML encode on the string; that is, if the value expanded to a < b, then the result would be a &lt; b. This is not what you would want to construct strings for URLs. You could tell the user to use the HTML expansion, but that is just an overhead and will generate more issues, like explaining why they need to use {{{ }}}.

To avoid this, Mustache has been set up so that it doesn't escape values when using {{ }}. This means that both {{ }} and {{{ }}} behave the same. This also leaves the encoding of any result as an exercise for the user if it's required.

The OOTB Mustache Model

Mustache requires a model to be applied to the template for expansion. In the preceding example, {{#content.date}} is already defined out-of-the-box, whereas a new property, such as {{person}}, can be added by the developer. If the user enters a value in the Mustache template that isn't in the model, the result will be an empty string. So, in the case of Hello {{person}}, it would expand to just Hello unless the developer adds person to the model.

The model object used is a global object called SCSMacros. The developer is free to add any additional entries into this object. The object will be passed to Mustache when the template is evaluated.

The out-of-the-box model object currently supports only the content.date object:

{   
   content: {
     date: <lambda implementation>
   }
}

Custom Mustache Model

The supported objects can be enhanced by the developer based on their requirements. So they can introduce a lastTwoDays object and simplify the expansion to just {{lastTwoDays}}.

To extend the model to support something like Hello {{person}} in the preceding example, you would need to add the person object to the SCSMacros. You need to do this before the page is run. It can be done within the page layout by adding a script tag to the start of it. For example:

<script type="text/javascript">
window.SCSMacros = window.SCSMacros || {};  // define/get the SCSMacros object
window.SCSMacros.person = "World";
</script>

After this change is made, the Hello {{person}} template would expand to: Hello World.

If you want to pass values to the object (for example, Hello {{#person}}personId{{/person}}), then you need to implement a mustache lambda and wrap and expand the value within the implementation.

For example:

<script type="text/javascript">
window.SCSMacros = window.SCSMacros || {};  // define/get the SCSMacros object
//implement "person" as a lambda
window.SCSMacros.person = function () {
  var people = { '111': { firstName: 'Small', lastName: 'World'}, '222': { firstName: 'Big', lastName: 'Universe'} };  
  return function (text, render) {     
     var expandedText = render(text);
     var chosenPerson = people[expandedText] || people['111'];      
     return chosenPerson.firstName;
  }
};
</script>

After this change is made, the Hello {{#person}}111{{/person}} template would expand to Hello Small, and the Hello {{#person}}222{{/person}} template would expand to Hello Big.

Note:

Mustache expansion executes synchronously. If you need to retrieve asynchronous values, these will need to be resolved within the model before you attempt to execute the mustache expansion, and this isn't currently supported, although bespoke implementations are possible.