The Power of Javascript Template Literals

The Power of Javascript Template Literals

Kurt Cagle 09/04/2018 1

Of all of the innovations that have emerged with the Ecmascript 2015+ rewrite of the Javascript language, one of the most powerful, template literals, is also one of the most under-appreciated. Template literals solve two problems - creating multiline content strings, and embedding variables in strings - but in conjunction with arrow functions and tagged literals, they can also solve many of the problems that up until now have been the province of specialized libraries.

Simplifying String Creation

A template literal is a string delimited by (indicated by) the backtick character (`), which on most keyboards shares space with the tilde (~). The use of this character has the immediate effect of vastly reducing the complexity of quoted content. Consider, for instance, the problem of creating a string of the following content:

"Really," said Alice,"you would think that this 'behavior' of his isn't
just an act."

This is the kind of string that programmers hate, because they either have to escape the content:

var str = "\"Really,\" said Alice,\"you would think that this 'behavior' 
of his isn\'t just an act.\"";

or you have to decompose it into substrings:

var str = '"Really," said Alice,"you would think that this '+"'behavior'
of his isn't just an act."+'"';

With a template literal, on the other hand, you bypass both of these problems:

var str = `"Really," said Alice,"you would think that this 'behavior' of 
his isn't just an act."`;

Now, the chances that you are likely to be writing dialog in your code is slim to none (if you are, you're probably doing something wrong), but anyone who writes inline functions within HTML knows how painful that process can be, because it's exactly the same thing:

var listItem = '<li class="foo"><a href="/prompt('+"'Enter your \
character\'s name:'"+')">Something</a></li>';

This is where template literals begin to earn their pay. Templates first can encompass both the single and double quote notations. However, more useful, the literal can also span multiple lines:

var listItem = `
<li class="oo">
    <a href="/prompt('Enter you character\'s name')">Something</a>
</li>
`;

You still have one escaped character, but the string above is both more legible and less likely to generate a syntax error. With inline HTML or XML, this can make coding easier, though of course, inline HTML is bad. Every developer knows this. Let me come back to that particular truism in a moment.

Inline Evaluation

The second use of template literals is to put inline content into strings, by using the ${} notation. For instance,

var firstName = "Jane";
var lastName = "Doe";
var bondName = `My name is ${lastName}. ${firstName} ${lastName}.`
console.log(bondName)
=> "My name is Doe. Jane Doe."

Now, by itself, this is interesting but largely superfluous - the assignment to a string variable with just defined sub-variables buys you nothing directly. However, this is where arrow functions come in. You can write a function that wraps a template entity:

function bondName(firstName, lastName){
    return `My name is ${lastName}. ${firstName} ${lastName}.`
    }

console.log(bondName("Jane","Doe"))

With arrow functions, on the other hand, you can make this less verbose:

var bondName = (firstName,lastName)=> 
     `My name is ${lastName}. ${firstName} ${lastName}.`

What's worth noting here is that content within the ${} is an expression, not just a variable. For instance, this example can made (marginally) more useful:

var name = {firstName:"Jane",lastName:"Doe"};
var bondName = (name)=> 
     `<li>My name is ${name.lastName}. 
          ${name.firstName} ${name.lastName}.</li>`

Now, suppose that rather than having a single name, you had an array of them:

var names = [
    {firstName:"Jane",lastName:"Doe"},
    {firstName:"John",lastName:"Dee"},
    {firstName,"James",lastName:"Bond"}
    ]

With this, you can make use of mapping functions, which are higher order methods that take functions as arguments.

var listItems = names.map((name)=> bondName(name))

This produces an array of strings.

console.log(listItems)
=> [
"<li>My name is Doe, Jane Doe.</li>",
"<li>My name is Dee, John Dee.</li>",
"<li>My name is Bond, James Bond.</li>"
]


The array can then be collapsed with a .join(" ") method, or the .reduce() method of arrays can be invoked instead. This can be used for a more complex expression:

var bondList = (names)=>`<ul data-count="${names.length}">
${listArray
    .map((name)=>bondName(name))
    .reduce((a,b)=> `${a}\n${b}`)
    }
</ul>`

This generates the output:

<ul data-count="3">
   <li>My name is Doe, Jane Doe.</li>
   <li>My name is Dee, John Dee.</li>
   <li>My name is Bond, James Bond.</li>
</ul> 

This can be taken even one step further, with the filter function. Let's say you only wanted an entry to print if the last name starts with a 'D' or "d". You could rewrite the above as:

var searchList = (names,prop,startsWith)=>{
    const re = new RegExp(`^${startsWith`,"i")
    const filterNames = names.filter((name)=>name[prop].match(re))
    return  
`<ul data-count="${filterNames.length}">
${filterNames
    .map((filterName)=>bondName(name))
    .reduce((a,b)=> `${a}\n${b}`,'') // This can also be .join('\n')
    }
</ul>`}

console.log(searchList(names,"lastName","d"))
---------------------
<ul data-count="2">
   <li>My name is Doe, Jane Doe.</li>
   <li>My name is Dee, John Dee.</li>
</ul> 

The syntax for the anonymous function is a bit different. If you are evaluating more than a single line of Javascript, you have to make sure the target anonymous function is contained in a {} block and the output is identified by a return keyword. By the way, this pattern is actually very familiar to anyone who works with XQuery. Replace

const a = ...
return b 

// with

let a : = ...
return b

and the patterns are almost identical. If you didn't need the count, this could be simplified further:

var reBegins = (startsWith)=> new RegExp(`^${startsWith}`,"i")
var searchList = (names,prop,re)=>
`<ul>
${names
    .filter((name)=> name[prop].match(re)))
    .map((filterName)=>bondName(name))
    .reduce((a,b)=> `${a}\n${b}`,'')
    }
</ul>`;

console.log(searchList(names,"lastName",reBegins("d")))
---------------------
<ul>
   <li>My name is Doe, Jane Doe.</li>
   <li>My name is Dee, John Dee.</li>
</ul> 

Notice how frequently template literal functions pop up here. They are especially useful with regex expressions that take input parameters, such as the one above that creates a regex that matches any string that starts with a given letter sequence, regardless of case.

var reBegins = (startsWith)=> new RegExp(`^${startsWith}`,"i")
console.log(reBegins("d"))
--------------
/^d/i

Now, the above can actual be decomposed into two three regexes mapped to an object:

var template = {
reBegins:(startsWith)=> new RegExp(`^${startsWith}`,"i"),
searchList:(names,prop,re)=>
`<ul>
${names
    .filter((name)=> name[prop].match(re)))
    .map((filterName)=>bondName(name))
    .reduce((a,b)=> `${a}\n${b}`,'')
    }
</ul>`,
bondName:(name)=> 
     `<li>My name is ${name.lastName}. 
          ${name.firstName} ${name.lastName}.</li>`
};

This can then be invoked as follows:

var output = template.searchList(names,"lastName",template.reBegins("d"))

Template Literals and Regexes

One benefit of this approach is that the template object can be used to pass functions in as a self contained unit. For instance, you could change the output by passing a different name function:

var template = {
reBegins:(startsWith)=> new RegExp(`^${startsWith}`,"i"),
searchList:(names,prop,re,nameFn)=>
`<ul>
${names
    .filter((name)=> name[prop].match(re)))
    .map((filterName)=>nameFn(name))
    .reduce((a,b)=> `${a}\n${b}`,'')
    }
</ul>`,
bondName:(name)=> 
     `<li>My name is ${name.lastName}. 
          ${name.firstName} ${name.lastName}.</li>`,
invertedName:(name)=> 
     `<li>${name.lastName}, ${name.firstName}</li>`,
};

Given this, if you wanted to get a list of inverted names (last name first) rather than bond names, you'd simply call this as:

template.searchList(
    names,
    "lastName",
    template.reBegins('d'),
    template.invertedName)

--------------------
<ul>
   <li>Doe, Jane</li>
   <li>Dee, John</li>
</ul> 

Rethinking Reactive Applications

This can dramatically simplify generation of content, and because the higher order methods are fast, this can be used to create content such as HTML far faster than DOM manipulations. Now, before raising the ire of many Javascript developers, I want to qualify that statement. If you create a list or table of items in HTML, then use the innerHTML method to write that table into the DOM and you do this every time a value in that table changes, your performance will be abysmal. The browser is in effect parsing text, creating a DOM tree, and inserting that DOM tree every time you make a change. It is in fact one of the major reasons why React and similar view layers exist - they create a virtual DOM that handles the manipulations, then only changes those things that have changed.

However, to do this, React typically also requires a binding library such as MobX or similar environment, which also adds complexity to the application. It has been my observation that when state changes on a given component, it is seldom a single property that changes but rather a complex of them, to the extent that it may be easier to in many cases to replace that single subcomponent (such as a card in an activity stream) rather than trying to change various properties.

One of the less well known methods for HTML is the insertAdjacentHTML() method, that works on any HTML or SVG element. This works on a given element and takes two arguments - a position argument and the HTML being inserted.

Each of the modes describes where the cursor is relative to a given element - beforeBegin (just before the element, like the blue box above, but outside of it), afterBegin (the point just inside the element at the beginning of a list in the blue box), beforeEnd (the point within the element after all other child elements) and afterEnd (outside and after the element).


This function is the core of the parsing and rendering system for the browser. It is fast. It is not (quite) as fast as insertion of a pre-parsed DOM, but either will be on the order of microseconds, far shorter than the overall lifecycle of the entity.

Now, there are still advantages to working with a reactive (as opposed to React) design. To the extent possible, any component that is created should be generated from a data structure distinct from the DOM itself. Put another way, there should be a distinct pipeline where mutations to the data source get reflected in the DOM, while form controls should initiate an event that changes the source, which then gets reflected back in the DOM.

However, there is also a fundamental problem with this approach, which comes with the need for editing a resource via a form. A form by its very nature must maintain an internal state, because until the draft resource has either been finalized or abandoned, it is not schematically consistent with any external data structure. This is one reason why React, which is focused primarily upon a finalized representation (and uses components primarily for navigation or selection), tends to become very complex when it has to deal with form content.

Revisiting the above example (which can be seen at here) the code here helps to illustrate the point:

// HTML

<div id="outer">
<div id="inner"></div>
</div>
// CSS

#outer {
  display:block;
  border:solid 2px red;
  width:250px;
}
#inner {
  display:block;
  border:solid 2px blue;
  width:245p6;
}
.card {
  display:grid;
  grid-direction:columns;
  grid-template-columns:auto 25px;
  border:inset 1px gray;
}
// JS

var elt=(selector)=>document.querySelector(selector);


class Adjacent {
  constructor(container){
    this.count=0;
    this.stack = [];
    this.containerName = container;
    this.container =elt(`#${container}`);
    window[container] = this;
   var content = this.appTemplate(container);
    document.body.insertAdjacentHTML("afterBegin",content)
  }

btnTemplate(container,mode){
  return `
   <td>
      <button onclick="${container}.addCard('${mode}')">${mode}</button>
   </td>`
}  
  
appTemplate(container){
  var btnTemplate = this.btnTemplate;
return `<table>
<tr>
  ${btnTemplate(container,"beforeBegin")}
  ${btnTemplate(container,"afterBegin")}
  ${btnTemplate(container,"beforeEnd")}
  ${btnTemplate(container,"afterEnd")}
</tr>
</table>`
}
cardTemplate(count,label,container){
  return `<div class="card" id="card${count}">
<div class="cardLabel">${label}</div>
<button onclick="${container}.deleteCard(event)">X</button>
</div>`
}  
  
addCard(mode,count){
  if (typeof mode === "object"){
    mode = mode.mode;
    count = mode.count;
  }
  var count = count||this.count++;
  var label = `Card ${count} - ${mode}`;
  this.stack.push({
    label:label,
    id:`card${count}`,
    count:count,
    mode:mode
  });
  var card = this.cardTemplate(this.count,label,this.containerName);
  this.container.insertAdjacentHTML(mode,card);
  this.count = Math.max(count,this.count);
  console.log(this.stack);
};

deleteCard(evt){
  var card = Array.from(evt.path).filter((elt)=>elt.className === "card")[0];
  var id = card.id;
  this.stack = this.stack.filter((card)=>card.id != id);
  card.remove(); 
  console.log(this.stack);
  }

load(data){
  data.forEach((card)=>this.addCard(card));
  }
}

new Adjacent("inner");



In this case, most of the logic takes place in the Adjacent class. There are three templates defined as methods: appTemplate, rowTemplate and cardTemplate. The app, when it initializes, creates a working variable in the window space (here called inner, based on the id of the <div> element that holds the inner content). The btnTemplate creates one of the four buttons at the top, which in turn invokes inner.addCard() with the appropriate modality ("beforeBegin", "afterBegin", etc.). This increments a counter, creates a label from the counter, then passes this to the cardTemplate to generate a view before inserting the result using that modality. The inner.deleteCard() method works similarly, by taking the event node and removing that from the list.

It's worth noting here that the operations, while occurring through the UI form, is also creating a record in the class. While not included here, a save method could save the stack object to an external data store, while a load method could take that saved stack, convert it from JSON and then iterate over each object with the forEach method, calling the addCard method each time:

load(data){
  data.forEach((card)=>this.addCard(card));
  }

This last point should probably be repeated - templates can be easily adapted to use objects as input, making creation of UI independent of whether the data comes from a user interface or from external input. The data is maintained by an independent object (here, the stackwithin the Adjacent() object), and this Adjacent class, rather than the HTML component or some global entity, manages this state.

Overall, template literals can be used to simplify separation of concerns, while still maintaining structural cohesiveness and, importantly, not having to create some intermediate quasi-HTML language to support this capability (which to me is a major limitation of React - it is hard to know without extensive experimentation what is and isn't supported).

This isn't everything that template literals can do. Tagged literals open up an additional powerful facility, though they deserve (and will get) their own article. In the meantine, you can check out Vue vs. Ember vs. Angular.js: Frameworks Comparison Guide

Share this article

Leave your comments

Post comment as a guest

0
terms and condition.
  • Patrick Vargas

    Good article !!

Share this article

Kurt Cagle

Tech Expert

Kurt is the founder and CEO of Semantical, LLC, a consulting company focusing on enterprise data hubs, metadata management, semantics, and NoSQL systems. He has developed large scale information and data governance strategies for Fortune 500 companies in the health care/insurance sector, media and entertainment, publishing, financial services and logistics arenas, as well as for government agencies in the defense and insurance sector (including the Affordable Care Act). Kurt holds a Bachelor of Science in Physics from the University of Illinois at Urbana–Champaign. 

   
Save
Cookies user prefences
We use cookies to ensure you to get the best experience on our website. If you decline the use of cookies, this website may not function as expected.
Accept all
Decline all
Read more
Analytics
Tools used to analyze the data to measure the effectiveness of a website and to understand how it works.
Google Analytics
Accept
Decline