Can't Render EJS Template on Client
Asked Answered
C

2

4

I'm coding an application on express, and I'm using ejs as a view/template engine.

At path /artists, I'm rendering the view artists.ejs which has artists covers. When clicking on a cover, I want an AJAX call to retrieve the corresponding data, place it in my template/view for artist artist.ejs and display this template in my HTML under the cover.

I've seen this related question but it has not solved my use case.

Everything seems clear, but I can't render the data with the template. I would like to compile the template server-side, send it to the client ready to use, and then fill it in when needed with the data received from the AJAX call.

What I've done:

When calling /artists, compile on server-side using ejs.compile(str, opt):

router.get('/artists', function(req, res) {

// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) { // Convert template file to string
    artist_template = ejs.compile(template); // Compile template

    res.render('artists.ejs', {template: artist_template}); // render page with compiled template
});

I took care of converting the file into String, as ejs compiler only works with String (compared to Jade .compileFile)

Then on client-side, I grab the function:

<script>
    var template = <%= template %>
</script>

Then on another script, I retrieve the data with an AJAX call:

$.get('/artists/'+artist_name, function(data) {
    var html = template({artist: data});
    $('#artist-page').html(html);
}

But when I make the call, I receive:

Uncaught ReferenceError: fn is not defined

When I call the template, fn, I receive:

Uncaught ReferenceError: opts is not defined.

Is the function fn hard-coded? I've read the EJS and Jade documentation but there was little relevant information in regards to my issue.

Do I perhaps need the template on client-side also?

Clout answered 13/10, 2016 at 14:28 Comment(0)
C
2

I eventually found a workaround to my question, and I understood with your answer that you could proceed in 2 different ways:

1) What I did: read and save template as a string, then render it client-side with ejs Runtime script.

// In controller.js    
var templates = {};
templates.template1 = fs.readFileSync(filePath1, 'utf-8'); // Read template as a string
templates.template2 = fs.readFileSync(filePath2, 'utf-8');     
...
res.render('app.ejs', {templates: templates}); // Send templates in view

// In view app.ejs
<script type="text/javascript">
   var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of strings) 
</script>
<script type="text/javascript" src="/JS/ejs.min.js"></script> <!-- Load ejs RunTime -->

// In site.js - javascript client/public file
$.get('/artists', function(data) {
     var html = ejs.render(templates.template1, data); // Render ejs client side with EJS script (template1 corresponds to the artists template)
     $('#artists-wrapper').html(html); // Sets HTML
});

Thus, I send all my templates on first page load, and then I render the requested page on the client side. The interest, according to what I've read, is that you only send JSON object (your data) through AJAX calls, and not the entire page, making your request light. Only the first load is heavy with all your templates.

2) What I would like to do according to @RyanZim answer: compiling templates server side into functions, send them, and then call them on the client side : template(data). If I understood well, there is no need of EJS client library in this case, and my templates are no longer strings but functions:

// In controller.js    
var templates = {};
templates.template1 = ejs.compile(fs.readFileSync(filePath1, 'utf-8'), {client: true}); // Get template as a function
templates.template2 = ejs.compile(fs.readFileSync(filePath2, 'utf-8'), {client: true});     
...
res.render('app.ejs', {templates: templates}); // Send templates in view

However, I can't get them in my view:

<script type="text/javascript">
   var templates = <%- JSON.stringify(templates) %>; // Get templates object (object of functions) 
</script>   

is not working. they are functions on the server before I send them, but I don't know how to recover them. Do you have an idea ?

I tried a workaround, by changing them into String before sending them:

templates.template1 = templates.template1.toString();

Send them and then client side, transform them back in functions:

var template = new Function(templates.template1);
$.get('/artists', function(data) {
     var html = template(data);
     $('#artists-wrapper').html(html); // Sets HTML
});

But that won't work either.

Do you have an idea what I'm missing here? And last, do you agree that compiling them server side before using the functions is better in terms of computation than rendering each template client-side?

Thanks for the help, and hope that will help anybody else!

Clout answered 14/6, 2017 at 17:36 Comment(3)
Firstly i have found your question very useful as I am in the process of trying the same thing.. Did you ever solve this ?Thayne
@Richlewis, hey thanks for your comment. Yep I managed to do it as I described in point 1), storing the templates as strings, then rendering them with EJS client-side .js. Have you tried it ?Clout
I have now managed to compile my html server side but getting into my view now is the next step https://mcmap.net/q/1164111/-render-ejs-node-jsThayne
W
1

You need to use the client option on the server side when you are compiling for the client. From the docs:

  • client When true, compiles a function that can be rendered in the browser without needing to load the EJS Runtime

https://github.com/mde/ejs#options

Your server-side code snippet should be:

// Compile artist template
fs.readFile('views/artist.ejs', "utf-8", function(err, template) {
  artist_template = ejs.compile(template, {client: true}); // Use client option

  res.render('artists.ejs', {template: artist_template});
});
Whirlabout answered 7/11, 2016 at 17:50 Comment(1)
Hey Ryan, thanks for your answer, I did the workaround explained in my answer below, and it is working well, but I also wanted to try this technique, compiling the template server side, and running it client-side, but impossible to make it run! Could you help me a bit on this issue ? Thanks!Clout

© 2022 - 2024 — McMap. All rights reserved.