Moment For Technology

Express Field (7) : Views and templates: Pug and EJS

Posted on Jan. 29, 2023, 7:25 a.m. by 白俊宏
Category: The back-end Tag: javascript node.js express


First, this article will discuss the use of the view template engine. With these template engines we can dynamically generate HTML content. Previously we have used EJS and implemented content injection using variable syntax. But this is just the tip of the iceberg of the template engine. Next, you'll learn about the various content injection methods, EJS, Pug, and other template engine features.

Express View Features

Before we begin, it's worth explaining what a View engine is. View engine as a programming term basically means "the module that renders the view". The two most commonly used view engines in the Express framework are Pug and EJS. It should be noted that the early name of Pug was Jade, which had to be changed for some reason.

Also, Express does not specify which engine must be used. As long as the view engine is designed to conform to the Express API specification, you can apply it to your project. Here's how it works.

Simple view rendering example

Let's review the EJS rendering process with a simple example:

var express = require("express");
var path = require("path");
var app = express();

app.set("view engine"."ejs");

app.set("views", path.resolve(__dirname, "views"));

app.get("/".function(req, res) {

app.listen(3000);Copy the code

Before you can run the code, you need to install EJS and Express via NPM Install. When you visit the application home page after installation, the application looks for views/index.ejs files and renders them using EJS. In addition, projects typically use only one view engine because multiple engines introduce unnecessary complexity into the project.

Complex view rendering

Let's look at a more complex example that uses both view engines, Pug and EJS:

var express = require("express");
var path = require("path");
var ejs = require("ejs");
var app = express();
app.locals.appName = "Song Lyrics";
app.set("view engine"."jade");
app.set("views", path.resolve(__dirname, "views"));
app.engine("html", ejs.renderFile);
app.use(function(req, res, next) {
    res.locals.userAgent = req.headers["user-agent"];
app.get("/about".function(req, res) {
    res.render("about", {
        currentUser: "india-arie123"
app.get("/contact".function(req, res) {
app.use(function(req, res) {
    res.render("404.html", {
        urlAttempted: req.url
app.listen(3000);Copy the code

Although the code looks complicated, the steps are actually quite simple when broken down. Let's examine the above code at render:

  1. Express creates the context object every time you call Render and passes it into the view engine when rendering. These context objects are actually variables that will be used in the view.

    Express starts by adding properties that already exist in app.local to the view that are common to all requests. Then add properties in res.locals and overwrite properties that might conflict with app.local. Finally, add the property at the Render call and possibly override it as well. For example, when accessing the /about path, the context object contains three properties: AppName, userAgent, currentUser; When the /contact path is accessed, the context object has only appName, userAgent; The attributes of the context object become appName, userAgent, urlatshed when the 404 is processed.

  2. Next, we will set whether to enable view caching. The view cache doesn't actually cache the view it actually caches the view path. For example, it caches views/my_views.ejs paths and binds them to the EJS engine.

    Express determines whether to cache view files in two ways:

    Document recording: Enable this function by calling app.enabled(" View cache"). This is disabled by default in development mode, but you can enable it in production. Of course, you can disable it manually via app.disable(" View cache").

    Non-documentation: Cache the file based on whether the cache object in the context of the first step is true. This allows you to customize the Settings for each file.

  3. Next, Express sets the view file name and the view engine it uses. You can skip to the last step if you have already done view caching in step 2. Otherwise, proceed to the next step.

  4. Complete view files missing extensions according to the default view engine. In this case,aboutWill be expanded intoabout.jadeAnd thecontact.ejsAs well as404.htmlThe file will remain the same. If you don't specify either the default view engine or the extension name, the application will crash.
  5. View engine matching by file extension. for.htmlFormat files are based onapp.engine("html", xx);Set to match.
  6. In the view query folder, find the file corresponding to the view file name. If it does not exist, an error is reported.
  7. Determine whether the found view file needs to be cached.
  8. Use the engine to render the view file and generate the final HTML file.

Using multiple view engines at the same time does add unnecessary complexity to your application, but fortunately we don't do that most of the time.

The content of Express's default response to the client is HTML. Most of the time this is fine, but sometimes you might want to return plain text, XML, JSON, and so on. At this point, you can customize the Settings by modifying the res.type parameter:

App. Get ("/",function(req, res) {res. Type (" text "); Res. Render (" myview ", {currentUser: "Gilligan}"); }Copy the code

Of course, you can use the simpler res.json

Express compatible setting for view engine: Consolidate.js

In addition to EJS and Pug, there are many mid-template engines. But these template engines may not be specifically designed for Express, as EJS and Pug are. If we need to use these unadapted template engines, we have to package consolidated. Js to be compatible with the Express API. In addition, consolidated. Js supports many kinds. You can see the complete list of supports on the project home page.

Let's say you're using an engine called Walrus, which is not compatible with Express. Then, let's see how Consolidate works.

First, use NPM install Walrus consolidate to install the related class libraries and dependencies, and then we'll introduce them:

var express = require("express");

var engines = require("consolidate");
var path = require("path");
var app = express();

app.set("view engine"."wal");
app.engine("wal", engines.walrus);
app.set("views", path.resolve(__dirname, "views"));

app.get("/".function(req, res) {
app.listen(3000);Copy the code

Isn't that easy? In just a few lines of code, we did the whole adaptation. Therefore, I strongly recommend that you Consolidate instead of doing it yourself when compatibility needs to be met.

What you have to know about EJS

EJS is one of the simplest and most popular view engines in Express. It can create templates for strings, HTML, plain text, and its integration is very simple. It works in both the browser and Node environment. It is very similar to ERB and method in Ruby.

There are actually two different versions of EJS maintained by different organizations. Although functionally similar, they are not the same class library. The EJS used in Express is maintained by TJ Holowaychuck, and you can find this class library through NPM. Another library of the same name stopped updating in 2009 and does not run in the Node environment.

EJS grammar

In addition to being used as an HTML template, it can be applied to strings and plain text. See how EJS renders the following text template:

Hi %= name %!
You were born in %= birthyear %, so that means you're %= (new Date()).getFullYear() - birthyear % years old. % if (career) { -% %=: career | capitalize % is a cool career! % } else { -% Haven't started a career yet? That's cool.
% } -%
Oh, let's read your bio: %- bio % See you later!Copy the code

Pass the following JSON data into the pad above:

    name: "Tony Hawk",
    birthyear: 1968,
    career: "skateboarding",
    bio: "bTony Hawk/b is the coolest skateboarder around."
}Copy the code

In the end, the rendered result is (assuming the current year is 2015) :

Hi Tony Hawk!
You were born in 1968, so that means you’re 47 years old.
Skateboarding is a cool career!
Oh, let'sread your bio: Tony Hawk is the coolest skateboarder around. See
you later!Copy the code

This example demonstrates the four syntax commonly used by EJS: print, print and escape, execute JS code, and filter.

In EJS you can print the value of an expression using two syntax: %= expression % and % -expression %, where the former will HTML escape the result. For example, when the expression value is passed in as Express, the former executes Express and the latter gets Express as a string. I suggest you use the former method because it is more reliable.

Also, EJS allows you to execute JS expressions in the % expression % syntax, and the expression is not printed. This feature is useful when performing loops and conditional judgments. Alternatively, you can avoid unnecessary line breaks with % expression -%.

Through the % = : expression | XXX % syntax, we can again the result of the expression filter processing. For example, above we apply an uppercase filter to the expression result. Of course, in addition to the numerous filters that come with it, you can also customize them.

Here, I made a sample EJS program. The interface isn't pretty, but you can get familiar with the various syntax uses of EJS.

Embed other EJS templates in existing EJS files

The EJS engine allows you to use another EJS template within the current template. So we can split and reuse the whole thing. For example, split the header and tail of HTML into header and footer modules and reuse them in other templates.

First we create header.ejs and copy the code:

! DOCTYPE html html head meta charset="utf-8"
    link rel="stylesheet" href="/the.css"
    title%= appTitle %/title
        h1%= appTitle %/h1
    /headerCopy the code

Create footer component footer.ejs and copy the code:

    All content copyright %= new Date().getFullYear() % %= appName %.
/htmlCopy the code

Finally, we do component embedding with include syntax:

% include header % h1Welcome to my page! /h1 pThis is a pretty cool page, I must say./p % include footer %Copy the code

If you want to implement a widget that displays user information, you can create the userWidget. ejs file and copy it:

div class="user-widget"
    img src="%= user.profilePicture %"
    div class="user-name"%= %/div
    div class="user-bio"%= %/div
/divCopy the code

Then, when rendering the current user, you can use the template like this:

% user = currentUser %
% include userwidget %Copy the code

Or when rendering a user list:

% userList.forEach(function(user) { %
  % include userwidget %
% } %Copy the code

With the include syntax in EJS, we can create templates and render them as components for subviews.

### Add your own filters The 22 filters built into Express include common operations on arrays and strings. Usually they will do more than you need, but sometimes you have to add your own filters.

Suppose you have now imported the EJS module and saved it in a variable named EJS. Then you can extend a filter for array summation for EJS. filters as follows.

ejs.filters.sum = function(arr) {
  var result = 0;
  for (var i = 0; i  arr.length; i++) {
    result += arr[i];
  return result;
};Copy the code

You can then use this filter in your code:

%=: myarray | sum %Copy the code

It's very simple to implement and use, so I recommend that you implement those common operations as filters.

What you need to know about Pug

View engines like Handlebars, Mustache, and EJS just extend the syntax of HTML and it doesn't break the syntax of HTML. This is great for a designer who knows HTML syntax and doesn't have to learn a new language. They also work in non-HTML template environments, which is a weakness of Pug.

But Pug also has its own unique advantages. It reduces the amount of code you need, and the code style is very good. Especially when writing HTML templates, tags are indented and do not need to be closed. In addition, EJS-style judgments and looping syntax are built in. There's a lot to learn, but it's also incredibly powerful.

Grammatical relation

Languages such as HTML are nested, with root elements () and sub-elements (such as and), while sub-elements can further nest other elements. In addition, HTML elements must be closed just like XML.

Pug uses a different indentation syntax. The following code shows a simple Web page implemented using Pug:

doctype html
    title Hello world!
    h1 This is a Pug example
      p Wow.Copy the code

The content in the above code will be converted to the following HTML.

! DOCTYPE html html lang="en" head titleHello world! /title /head body h1This is a Pug example/h1 div id="container"
/htmlCopy the code

You can go to the Pug project home page to see how it made this transition.

The layout of the relation

Layout is an important feature of all template languages. It allows us to implement common components and reuse them in other files. For example, we can separate the header and footer from the page. This not only ensures the same header and footer content across all pages, but also makes it easier to modify.

The implementation steps of Pug layout are as follows:

The first step is to define a master layout file for all pages, which is almost an empty template. It uses block syntax for placeholders, which are then replaced with content by the actual generated page. The following is an example:

doctype html
    title Cute Animals website
    link(rel="stylesheet" href="the.css")
    block header  
    h1 Cute Animals website
    block bodyCopy the code

You can see that header and body placeholders are defined. Let's save it to a layout.jade file. Next we implement the body block:

extends layout.jade
block body
  p Welcome to my cute animals page!Copy the code

Layout. jade will be rendered as:

! DOCTYPE html html head meta charset="utf-8"
    titleCute Animals website/title
    link rel="stylesheet" href="the.css" /head body h1Cute Animals website/h1 pWelcome to my cute animals page! /p /body /htmlCopy the code

Note that when you expand the main layout, you don't have to implement all of the placeholders. For example, the header is not implemented above.

The body block can be implemented differently on other pages:

extends layout.jade
block body
  p This is another page using ths layout.
  img(src="cute_dog.jpg" alt="A cute dog!")
  p Isn't that a cute dog!Copy the code

Pug's separation of components through layout allows us to avoid repetitive code.

Mixins for Pug

There's another cool feature in PUGs called Mixins. This feature allows you to define functions in a file that may need to be used over and over again. Here, we use this feature to re-implement the previous EJS part of the user information display function:

Mixin user-widget(user).user-widget img(SRC = user.profilepicture).user-name= user-bio= // Displays the current user +user-widget(currentUser) // Display user list - each userin userList 
  +user-widget(user)Copy the code

So much for the basics of Pug, see the official documentation for more syntax details.


This chapter covers:

  • Express's view system and how it renders dynamically.
  • EJS engine syntax and basic usage.
  • Pug engine syntax and basic usage.

The original address

About (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.