Screenshot from 2024-02-02 17-13-34.png

Javascript HTML Formatting - How to Use Underscore Templates

If you're looking to create dynamic and interactive web applications easily, you've come to the right place. Underscore.js is a powerful JavaScript library that, among its many features, offers a simple yet robust templating engine. The best part? On Appsmith, Underscore.js is preloaded and ready to use, allowing us to focus on building our application without worrying about setup and configuration. Fast, easy, and powerful - what's not to like?

If you are impatient and want to just see it in action, we have a Star Wars Quote template here.

Underscore.js provides a cool _.template() method that lets you compose reusable components and HTML fragments that make it easy to reuse common code and make changes down the line.

"But, wait! In Appsmith, we are using the canvas and widgets to control the UI - how will _.template() help me?"

Excellent question, dear reader - and I'm glad you asked! While it is true that most of the widgets provide their own styling and options for repeating patterns (looking at you, List widget), there are actually a number of times when we can create custom HTML for output:

  1. Text widget - did you know that you can include many HTML tags in the text widget? Not only common things like strong, ul, and p... but also things like audio, blockquote, and details!
  2. Iframe widget - good 'ole iframe widget - useful for creating your own mini page (or app) inside your app. This is open to pretty much everything, and works with both externally loaded HTML as well as HTML you load into the widget. So - you can go pretty crazy with it.
  3. Custom widget - have you heard about the custom widget yet? This takes the iframe widget to the next level by providing a better UI and the ability to pass data in and out of the widget easily. 

Note: the text widget will apply some additional formatting and spacing that may interfere with your desired output. You can either use inline styles to adjust, or just use an iframe or custom widget

Understanding _.template() in Underscore.js

The _.template() function in Underscore.js is designed to compile JavaScript templates into functions. These functions can then be executed to render HTML content dynamically. 

Syntax:

_.template(templateString, [settings])

Parameters:

  • templateString: This is the string that contains the HTML template you wish to render. It can include placeholders for data insertion.
  • settings (optional): An object that allows you to override any default _.templateSettings. This can be used to customize how templates are compiled and rendered.

All you need to do is create the template, define it as a function, and then call that function with any parameters you need. The basics are super simple. Let's look at a quick example:

// First, create the template with the placeholders
let quoteTemplate = '<blockquote><%= quote %><br> -- <%= person %></blockquote>';

// Compiling the template
let renderQuote = _.template(quoteTemplate);

// Rendering the template with provided data
let renderedQuote = renderQuote({quote: 'Hello there!', person: 'Obi-Wan Kenobi'});

// Output the rendered HTML string
console.log(renderedQuote);

Basic Usage and Tags

Ok, that looks good, but whats all the <% stuff? Underscore templates support three special types of tags for different purposes:

  1. Interpolate (<%= … %>)
    • Used to print the value of a variable or the result of an expression within the template. It directly inserts the specified content.
  2. Evaluate (<% … %>)
    • Allows for the execution of arbitrary JavaScript code within the template. This is particularly useful for adding logic (like loops or conditionals) to your templates.
  3. Escape (<%- … %>)
    • Similar to the interpolate tag, but it also escapes HTML-specific characters (such as &, <, >, ", and '). This is essential for preventing XSS (Cross-Site Scripting) attacks by sanitizing user-generated content.

Examples:

// Value to be printed 
<%= user.name %>

// Executable JavaScript code, in this case a loop 
<% for (var i = 0; i < items.length; i++) { %> ... <% } %>

// HTML-escaped value to be printed: 
<%- user.input %>

By leveraging these tags, you can craft dynamic templates that are both secure and versatile, enabling the creation of extensive HTML.

Putting it into action

Ok - so now we have a basic grasp of the concept. Let's see how it works. We will create some HTML that displays a character image, name, and list of quotes. The HTML can be rendered in an Iframe or Text widget.

First, we need some type of data. So, lets create a data JSObject that holds a list of Star Wars characters, with a name, image, and quotes for each. An example is here:

  yoda: {
    name: "Yoda",
    image: "https://static.wikia.nocookie.net/starwars/images/c/c3/Yoda_TPM_RotS.png",
    quotes: [
      "Do. Or do not. There is no try.",
      "You must unlearn what you have learned.",
      "When nine hundred years old you reach, look as good you will not.",
      "Truly wonderful, the mind of a child is.",
      "A Jedi uses the Force for knowledge and defense, never for attack.",
      "Adventure. Excitement. A Jedi craves not these things.",
      "Size matters not. Judge me by my size, do you?",
      "Fear is the path to the dark side... fear leads to anger... anger leads to hate... hate leads to suffering.",
      "Wars not make one great.",
      "Luminous beings are we... not this crude matter.",
      "Difficult to see. Always in motion is the future.",
      "Control, control, you must learn control!"
    ]
  },

Next, let's create a template JSObject, and create the template we want to render the data:

	// Define the template string with placeholders and inline CSS
	characterTemplateString : `
		<h2 style="text-align: center;">
			<%= name %>
		</h2>
		<div style="display: flex; align-items: flex-start;">
			<div style="flex: 50% 1 1">
				<img src="<%= image %>" alt="<%= name %>" style="width: 100%; height: auto;">
			</div>
			<div style="flex: 50% 1 1; padding: 1rem;">
				<ul>
					<% _.each(quotes, function(quote) { %>
						<li><%= quote %></li>
					<% }); %>
				</ul>
			</div>
		</div>
	`

Note that we are using inline styles for some basic theming. For more control, you can use a custom widget with full CSS available. You will also see that we are using <%= name %> so we can output the name from the object above. We are embedding a JS loop using <%to wrap the code and another Underscore method, _.each(). This will create an <li> for each quote. 

Now, for the fun part. We will create a render() method that will parse the template and return the HTML:

	render() {
		// Use the currently selected character to render
		let characterData = data[controller.selectedCharacter];
		
		// Compile the template string into a function
		let characterTemplate = _.template(this.characterTemplateString)
		let renderedHTML = characterTemplate(characterData);
		
		return renderedHTML;
	}

Here, we are taking the default value for the selected character from the controller JSObject (not shown). This will be tied to a select list so you can change the character and regenerate the HTML with different values on the fly. Then, we compile the template into a new function with _.template(), and use that to render the HTML. Simple as that!

There is a companion Star Wars Quote template to show you how to do this with more data and allow you to change the character. You can fork that and play around with it a bit. Have fun!

Fri, 02/02/2024 - 18:25

Is this available only in a single app or workspace or would it be universally accessible?  My goal is to use this template in multiple Apps.

Ron Northcutt staff View ron's profile
Mon, 02/12/2024 - 11:52

In reply to by pchambless1

This is all JS, so you can easily create a custom JS library to share the template across any number of apps. The community edition will be releasing Building Block Templates in the next quarter or so, and you could easily use building blocks with a JS library. The commercial edition is releasing the beta for reusable JS soon, which will preclude the need for custom JS libraries (which need to be public). 

Lots of options!