Express/Node AJAX templating

I wanted to try passing data through handlebars and serving the resulting HTML in response to AJAX calls. This was just a test.


app.get('/profile', function(req, res) {
  var people = [{name: "Oliver", age: 23}, {name: "Mark", age: 60}]
  var markup = "";
  
  people.forEach((person) => {
    app.render('profile', person, function(error, html) {
      if (error) { console.log(error)}
      else {
        markup += html;
      }
    })
  })
  console.log(markup);
  res.send(markup)  
})

The markup variable is always null and I can’t work out why.

fetch('/profile')
.then(response => response.text())
.then(text => {console.log(text)})

A couple of things occur to me:

  1. app.render('profile', person, function(error, html) {
    should probably be
    app.render('profile', { person }, function(error, html) {
    at least, that was what worked for me using express-handlebars.
  2. The call to app.render seems to be asynchronous (which surprised me). You can test this like so:
    setTimeout(() => { res.send(markup) }, 50);

If that is indeed the case, a better solution seemed to be to send the markup once you have processed the last person. This worked as expected for me:

const express = require('express');
const path = require('path');
const exphbs  = require('express-handlebars');
 
const app = express();
 
app.set('views', path.join(__dirname, 'views'));
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');

app.get('/profile', (req, res) => {
  const people = [{name: "Oliver", age: 23}, {name: "Mark", age: 60}];
  let markup = "";

  people.forEach((person, i) => {
    app.render('profile', { person }, function(error, html) {
      if (error) { console.log(error); }
      else {
        markup += html;
      }

      if(i === people.length - 1) res.send(markup);
    });
  });
});

app.listen(3000, () => console.log('Example app listening on port 3000!'));

This still seems hacky, though.

2 Likes

Yeah, I guess the cleanest solution would be to iterate over people inside the template, keeping such view logic out of the controller and all:

<ul>
  {{#each people}}
  <li>{{this.name}} -- {{this.age}}</li>
  {{/each}}
</ul>
app.get('/profile', (req, res) => {
  const people = getPeopleSomewhere()
  res.render('profile', { people })
})
2 Likes

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.