Skip to content

Instantly share code, notes, and snippets.

@robwormald
Last active June 17, 2019 00:33
Show Gist options
  • Save robwormald/631e92017c5b070e368c235d2dc280df to your computer and use it in GitHub Desktop.
Save robwormald/631e92017c5b070e368c235d2dc280df to your computer and use it in GitHub Desktop.

Thinking about tagged template literals and HTML templating.

(Disclaimer: I work on the Angular team, but this isn't something the Angular team is considering (yet). This is simply me capturing some thoughts...)

ES2015 added Template Literals to JavaScript (Template Literals is the current term, they were known as "Template Strings" in the first edition)

They're useful for all kinds of things, the most obvious of which is interpolating values in a much more concise way than concatting strings together:

const greeting = `Hello ${name}!`;
//nice with arrow functions
const greet = name => `Hello ${name}!`;

greet('rob'); //Hello rob!

The other, lesser-known API that came along with Template Literals is Tagged Templates.

The idea here is you can "tag" a template literal with a function - effectively allowing you to parse the string and interpolated values yourself:

function format(strings, ...values){
  //join the static strings + values ourselves
  return strings.reduce((all, part, index) =>  {
    //for each static string, check if there's an interpolated value that follows and join
    const name = values[index];
    if(!name) return all + part;
    return `${all}${part}${name.charAt(0).toUpperCase()}${name.slice(1)}` 
  }, '');
}

const greet = name => format`Hello ${name}!`
greet('rob') //Hello Rob!

Wrapping my brain around this idea took a while, but the key insight for me was that tagged templates are just a different syntax for invoking your tag function:

const formatted = format`Hello ${'rob'}!`

is just a shorthand for:

const formatted = format(['Hello ', '!'], ['rob']);

(This isn't strictly true, and why that is we'll get into shortly)

Another interesting thing about tagged template literals is that your tag function doesn't necessarily have to return a string. Maybe you want to do something clever with the various parts of the string - you could return the raw parts and values in a data-structure, and wire them up later:

//our tag just captures the strings + values
function localized(strings, ...values){
  return [strings, values];
}

function localize(data, locale){
  const formatter = new Intl.DateTimeFormat(locale);
  const [strings, values] = data;
  
  return strings.reduce((all, str, i) => {
    return `${all}${str}${values[i] ? formatter.format(values[i]) : ''}`
  }, '');
}

const dateGreeting = date => localized`The current date is ${date}!`;

const currentDate = new Date();

console.log(localize(dateGreeting(currentDate), 'en-US')); //The current date is 6/16/2019!
console.log(localize(dateGreeting(currentDate), 'en-GB')); //The current date is 16/06/2019!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment