5

I want to call some of my functions which are asynchronous to be called from within my ejs files.

Like I have this functions set as my app.locals.someFunction

async someFunction(param){
  let data = await someAsyncStuff();
  // &  other things
  return data;
}

and I want to use it inside ejs file as below:

<%
let data = await someFunction()
for(let x of data){%>

   <li><%=x%></li>

<%}%>

This is possible with ejs if {async:true} is passed as an option. But where exactly should I pass this when my view engine setup is like the following?


//view engine setup
app.engine ('.html', ejs.renderFile);
app.set ('view engine', 'html');

Imtiaz Chowdhury
  • 149
  • 1
  • 1
  • 11

3 Answers3

17

instead res.render()

const ejs = require('ejs');
const html = await ejs.renderFile(view, data, {async: true});
res.send(html);

every include with await

<body>
    <%- await include(pageView);%>
</body>

async now fine

<%
let data = await collection.find().toArray();

for(let x of data){%>

   <li><%=x%></li>

<%}%>
2

You have to pass the argument async into your render() function call as opts. E.g.

res.render(view, {.., async:true, ... }...) 
phaen
  • 349
  • 2
  • 12
2

For people who are still trying to figure out how to implement async ejs templates, I am listing my complete solution below:

In your main app file (app.js)

// app.js
// Set ejs as view engine
app.set('view engine', 'ejs');

let ejsOptions = {
  // delimiter: '?', Adding this to tell you do NOT use this like I've seen in other docs, does not work for Express 4
  async: true
};

// The engine is using a callback method for async rendering
app.engine('ejs', async (path, data, cb) => {
  try{
    let html = await ejs.renderFile(path, data, ejsOptions);
    cb(null, html);
  }catch (e){
    cb(e, '');
  }
});

Now for your route responses...

app.route('/login')
  .get( async (req, res) =>{
      // layout.ejs is my version of blocking. I pass the page name as an option to render custom pages in the template
      return await res.render(`layout.ejs`, { page : 'login' }, (err, html) => standardResponse(err, html, res));
})

I user the function standardResponse() in the callback for both readability and so I have to write less lines. The function works like this...

const standardResponse = (err, html, res) => {
  // If error, return 500 page
  if (err) {
    console.log(err);
    // Passing null to the error response to avoid infinite loops XP
    return res.status(500).render(`layout.ejs`, { page : '500', error: err }, (err, html) => standardResponse(null, html, res));
  // Otherwise return the html
  } else {
    return res.status(200).send(html);
  }
}

And viola! Asynchronous ejs rendering.

booellean
  • 71
  • 7