Skip to main content

Express Data Passing

Introduction

When building web applications with Express.js, one of the most important concepts to understand is how to pass data from your server to your templates. This process allows you to create dynamic web pages that can display different content based on user interactions, database queries, or any other variable data.

Data passing is what transforms static HTML templates into dynamic views that can react and adapt to different situations. Without it, your web applications would be limited to displaying the same content to every user, regardless of their needs or interactions.

In this lesson, we'll explore the various methods for passing data from your Express routes to your templates, whether you're using EJS, Handlebars, or Pug.

Why Pass Data to Templates?

Before diving into the implementation details, let's understand why data passing is essential:

  1. Dynamic Content: Display user-specific information or content that changes based on various conditions
  2. Reusable Templates: Create a single template that can be reused with different data
  3. Separation of Concerns: Keep your data handling logic in your routes and your presentation logic in your templates

Basic Data Passing

The most common way to pass data to a template in Express is through the render method's second argument. Here's a basic example:

javascript
app.get('/greeting', (req, res) => {
res.render('greeting', { name: 'John', time: new Date().toTimeString() });
});

In this example, we're passing an object with two properties: name and time. These properties will be available in the template when it's rendered.

How It Looks in Different Template Engines

Let's see how we would access this data in different templating engines:

In EJS:

ejs
<h1>Hello, <%= name %>!</h1>
<p>The current time is: <%= time %></p>

In Handlebars:

handlebars
<h1>Hello, {{name}}!</h1>
<p>The current time is: {{time}}</p>

In Pug:

pug
h1 Hello, #{name}!
p The current time is: #{time}

Output:

Regardless of which template engine you use, the rendered HTML would look something like this:

html
<h1>Hello, John!</h1>
<p>The current time is: 14:30:45 GMT+0000 (Coordinated Universal Time)</p>

Passing Complex Data Structures

You can pass more complex data structures like arrays and nested objects to your templates.

Example with Arrays:

javascript
app.get('/shopping-list', (req, res) => {
const items = ['Apples', 'Bread', 'Milk', 'Eggs'];
res.render('shopping-list', { items: items });
});

In EJS:

ejs
<h1>Shopping List</h1>
<ul>
<% items.forEach(function(item) { %>
<li><%= item %></li>
<% }); %>
</ul>

Example with Nested Objects:

javascript
app.get('/user-profile', (req, res) => {
const user = {
name: 'Jane Doe',
email: '[email protected]',
address: {
street: '123 Main St',
city: 'Anytown',
zipcode: '12345'
},
hobbies: ['Reading', 'Hiking', 'Cooking']
};

res.render('profile', { user: user });
});

In EJS:

ejs
<h1><%= user.name %>'s Profile</h1>
<p>Email: <%= user.email %></p>

<h2>Address:</h2>
<address>
<%= user.address.street %><br>
<%= user.address.city %>, <%= user.address.zipcode %>
</address>

<h2>Hobbies:</h2>
<ul>
<% user.hobbies.forEach(function(hobby) { %>
<li><%= hobby %></li>
<% }); %>
</ul>

Conditional Rendering

You can pass boolean values to conditionally render parts of your template:

javascript
app.get('/dashboard', (req, res) => {
const user = {
name: 'Sarah',
isAdmin: true,
notifications: 3
};

res.render('dashboard', { user });
});

In EJS:

ejs
<h1>Welcome, <%= user.name %></h1>

<% if (user.isAdmin) { %>
<div class="admin-panel">
<h2>Admin Panel</h2>
<button>Manage Users</button>
<button>Site Settings</button>
</div>
<% } %>

<% if (user.notifications > 0) { %>
<div class="notification-badge">
You have <%= user.notifications %> new notifications
</div>
<% } %>

Passing Functions

You can also pass functions to your templates, which can be useful for formatting data or performing calculations:

javascript
app.get('/calculator', (req, res) => {
const helpers = {
formatCurrency: (amount) => {
return `$${amount.toFixed(2)}`;
},
calculateTotal: (items) => {
return items.reduce((total, item) => total + item.price, 0);
}
};

const cart = [
{ name: 'Notebook', price: 9.99 },
{ name: 'Pens', price: 5.49 },
{ name: 'Highlighter', price: 3.99 }
];

res.render('calculator', { helpers, cart });
});

In EJS:

ejs
<h1>Shopping Cart</h1>

<ul>
<% cart.forEach(function(item) { %>
<li><%= item.name %>: <%= helpers.formatCurrency(item.price) %></li>
<% }); %>
</ul>

<h2>Total: <%= helpers.formatCurrency(helpers.calculateTotal(cart)) %></h2>

Real-World Application: Dynamic Blog Post

Let's put everything together in a more comprehensive example. Imagine building a simple blog:

javascript
app.get('/blog/:postId', (req, res) => {
// In a real app, you'd fetch this from a database
const post = {
id: req.params.postId,
title: 'Understanding Express Data Passing',
author: {
name: 'Developer Dave',
avatar: '/images/dave.jpg'
},
date: new Date('2023-04-15'),
content: 'Express makes it easy to pass data to templates...',
tags: ['express', 'nodejs', 'templates'],
comments: [
{ user: 'Sarah', text: 'Great article!', date: new Date('2023-04-16') },
{ user: 'Mike', text: 'This helped me understand templates!', date: new Date('2023-04-17') }
]
};

// Helper functions
const helpers = {
formatDate: (date) => {
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
};

// Check if the post exists
const postExists = req.params.postId === '1'; // Simplified check

res.render('blog-post', { post, helpers, postExists });
});

In EJS:

ejs
<% if (!postExists) { %>
<div class="error">
<h1>Post Not Found</h1>
<p>Sorry, the blog post you're looking for doesn't exist.</p>
<a href="/blog">Return to Blog Home</a>
</div>
<% } else { %>
<article class="blog-post">
<header>
<h1><%= post.title %></h1>
<div class="author-info">
<img src="<%= post.author.avatar %>" alt="<%= post.author.name %>" class="avatar">
<span>By <%= post.author.name %> on <%= helpers.formatDate(post.date) %></span>
</div>

<div class="tags">
<% post.tags.forEach(function(tag) { %>
<span class="tag"><%= tag %></span>
<% }); %>
</div>
</header>

<div class="content">
<%= post.content %>
</div>

<section class="comments">
<h2><%= post.comments.length %> Comments</h2>

<% post.comments.forEach(function(comment) { %>
<div class="comment">
<strong><%= comment.user %></strong> said on <%= helpers.formatDate(comment.date) %>:
<p><%= comment.text %></p>
</div>
<% }); %>
</section>
</article>
<% } %>

Common Data Passing Patterns

Here are some common patterns when passing data to templates:

1. Global Data for All Templates

If you want certain data to be available to all templates, you can use middleware:

javascript
// Set up global template variables
app.use((req, res, next) => {
res.locals.siteName = 'My Awesome Website';
res.locals.currentYear = new Date().getFullYear();
res.locals.isLoggedIn = req.session.user ? true : false;
next();
});

// Now, in any route, these variables are available
app.get('/about', (req, res) => {
// No need to pass siteName, currentYear, or isLoggedIn here
res.render('about', { pageTitle: 'About Us' });
});

2. Flash Messages

For passing one-time messages between requests (like success or error messages after a form submission):

javascript
app.post('/contact', (req, res) => {
// Process form submission
// ...

req.flash('success', 'Your message has been sent!');
res.redirect('/contact');
});

app.get('/contact', (req, res) => {
res.render('contact', {
messages: {
success: req.flash('success'),
error: req.flash('error')
}
});
});

In your template:

ejs
<% if (messages.success.length > 0) { %>
<div class="alert success">
<%= messages.success %>
</div>
<% } %>

<% if (messages.error.length > 0) { %>
<div class="alert error">
<%= messages.error %>
</div>
<% } %>

Best Practices for Data Passing

  1. Keep It Organized: Structure your data logically and consistently
  2. Don't Pass Too Much: Only pass the data that your template needs
  3. Use Helpers for Formatting: Keep complex formatting logic out of templates
  4. Error Handling: Always handle cases where data might be missing
  5. Security: Never pass sensitive information to templates that shouldn't be exposed to users

Summary

Data passing is a fundamental concept in Express templating that allows you to create dynamic, responsive web applications. By sending data from your routes to your templates, you can:

  • Display user-specific content
  • Render dynamic lists and complex objects
  • Conditionally show or hide elements
  • Format data for display
  • Create reusable template components

The basic pattern is to include an object as the second parameter to the res.render() method, and then access those properties within your template using the syntax specific to your templating engine.

Remember that different templating engines (EJS, Handlebars, Pug) have slightly different syntax for accessing this data, but the core concept remains the same.

Exercises

  1. Create a route that passes an array of products to a template and renders them in a grid with details like name, price, and description.

  2. Build a user profile page that displays different sections based on the user's role (admin, regular user, guest).

  3. Create a dashboard that shows different data visualizations based on data passed from the server.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)