Skip to main content Skip to footer

Dynamic Template Literals

In the 2019 v3 release of Wijmo, we added a cellTemplate property to the FlexGrid columns. This property allows developers to use template literal expressions to generate content for cells.

Read the full Wijmo v3 release

The nice thing about using template literal expressions is that they support calculations, which makes is possible to apply conditional formatting to cells without writing actual JavaScript code. For example:

// simple/default rendering with template string
col.cellTemplate = '${value}:${col.format}';

// conditional formatting with template string
col.cellTemplate =
  '<span class=${value > 40000 ? "big-val" : "small-val"}>${text}</span>';

Note that although we are using template literals, the property is specified as a standard string (no back-ticks). This is necessary because we don't want to evaluate the templates when assigning the property. We want to evaluate them later, when the cells are rendered.

To accomplish this, we added an evalTemplate function to Wijmo. The function takes a template string containing the literal template expression and a context object that provides the variables for evaluating the expression.

We think this is a common scenario, so the function is public, and you can use it in your apps.

The evalTemplate function provides two additional bonuses:

  1. If has built-in globalization/formatting, courtesy of Wijmo's glbz tag function, and
  2. It works in legacy browsers that don't support template literals (IE).

In case you're interested in how this works, here's the source code for Wijmo's evalTemplate function:

export function evalTemplate(template: string, ctx?: any): string {
  if (isIE()) {
    //
    // in IE, use a regex to extract the template items and format strings
    // from the template string:
    let rx = /\${([^}]*)}(:(([A-Za-z]\d*)|"([^"]+)"|'([^']+)'))?/g; 
    return template.replace(rx, (t, expr, f, fmt, fRaw, fQ2, fQ1) => {
        //
        // calculate the resule
        let result = _evalExpression(expr, ctx);
        //
        // format the result (plays the role of glbz)
        return fmt ? Globalize.format(result, fRaw || fQ2 || fQ1) : result;
    });
  } else {
      //
      // not IE, let JavaScript and glbz evaluate the template string
      return _evalExpression(template, ctx)
  }
}
function _evalExpression(expr: string, ctx: any): string {
  //
  // make sure we have a template, include the glbz tag function
  ctx = ctx || {};
  ctx.glbz = glbz;
  //
  // get keys and values from context object
  let keys = Object.keys(ctx);
  let vals = keys.map(e => ctx[e]);
  //
  // build a function to evaluate the template literal 
  let fn = isIE()
    ? new Function(...keys, 'return ' + expr)
    : new Function(...keys, 'return glbz`' + expr + '`');
  //
  // call the function and return the result
  return fn(...vals);
}

The code should be self-explanatory. It is quite concise for what it does.

Supporting dynamic template literals in IE is something we're proud of. Although IE is no longer a popular browser, we know that some 5% of users still rely on it (that percentage is closer to 20% in some countries).

We hope you find this solution interesting and useful as well.

comments powered by Disqus