Client side and Server side rendering of ejs template
Asked Answered
N

4

7

I always wanted to learn NodeJS to be able to run the same code on server and client side. I am using NodeJS with Express and EJS. So. I have a .ejs page with lot's of HTML, JS, CSS and a small bit with template. For the sake of justice let it be like this:

the_list-->some.ejs

<ul> 
<% for(i=0;i>the_list.length;i++) { %>
    <li>the_list[i]</li>
<% } %>
</ul>    

After some rendering on the server we have a perfect list.

So. Now I want to rerender it on the client. I made some ajax request and now I have new items in the_list. What is the right way?

Newsmagazine answered 6/12, 2016 at 17:44 Comment(0)
F
6

As per ejs templates documentation

var template = new EJS({
  text: `
    <ul>
      <% for(i = 0; i < the_list.length; i++) { %>
        <li>the_list[i]</li>
      <% } %>
    </ul>
  `
});
var html = template.render({ the_list: data });
document.getElementById('list-wrapper').innerHTML = html;
Favin answered 6/12, 2016 at 17:58 Comment(0)
K
5
<div id="output"></div>
<script src="/assets/js/ejs.js"></script>
<script>
  let blogPosts = [
    {
        title: 'Perk is for real!',
        body: '...',
        author: 'Aaron Larner',
        publishedAt: new Date('2016-03-19'),
        createdAt: new Date('2016-03-19')
    },
    {
        title: 'Development continues...',
        body: '...',
        author: 'Aaron Larner',
        publishedAt: new Date('2016-03-18'),
        createdAt: new Date('2016-03-18')
    },
    {
        title: 'Welcome to Perk!',
        body: '...',
        author: 'Aaron Larner',
        publishedAt: new Date('2016-03-17'),
        createdAt: new Date('2016-03-17')
    }
];
      var html = ejs.render(`<% for(let i = 0; i < posts.length; i++) { %>
    <article>
        <h2><%= posts[i].title %></h1>
        <p><%= posts[i].body %></p>
    </article>
<% } %>`, {posts: blogPosts});
  // Vanilla JS:
  document.getElementById('output').innerHTML = html;
</script>

download ejs.js or ejs.min.js from latest version

Karinakarine answered 23/5, 2017 at 13:53 Comment(1)
I have a question similar to this #50366723 but i want to use a partial file rather than put my HTML as above.. Do you know if this is possible at all ? thanksUrogenital
A
0

Sure, EJS works on the client. You can trivially keep the template in a string variable or apply EJS to user-provided input, but more likely, you'll want to store a template in a script (which can be in an external file) or use fetch to grab your template from another file on demand.

Using a template in a <script> is straightforward:

const people = ["geddy", "neil", "alex"];

const template = document
  .querySelector("#template")
  .innerText;

document.querySelector("#output")
  .innerHTML = ejs.render(template, {people});
<!-- could be an external file -->
<script id="template" type="text/template">
<%= people.join(", "); %>
</script>

<div id="output"></div>
<script src="https://unpkg.com/[email protected]/ejs.min.js"></script>

For fetch, I'll mock the response so it'll be runnable in a snippet:

// mock fetch for illustrative purposes; 
// its response content would be another file
fetch = async url => ({text: async () => '<%= people.join(", "); %>'});

fetch("/your-template")
  .then(res => res.text())
  .then(template => {
    const people = ["geddy", "neil", "alex"];
    document.querySelector("#output").innerHTML = 
      ejs.render(template, {people});    
  });
<script src="https://unpkg.com/[email protected]/ejs.min.js"></script>
<div id="output"></div>

If this seems like too much heavy lifting, you can bury the fetch in a helper function, or go a step further and pick an attribute for each URL, then plug everything in with a call to a library function you can abstract away from the main code. A simple example:

// mock fetch for illustrative purposes; 
// its response content would be in other files
const responses = {
  "/template.ejs": "<%= 42 %>",
  "/other-template.ejs": "<%= 43 %>",
};
fetch = async url => ({text: async () => responses[url]});

[...document.querySelectorAll("[data-template]")]
  .forEach(e => {
    fetch(e.getAttribute("data-template"))
      .then(res => res.text())
      .then(template => {
        e.innerHTML = ejs.render(template);
      });
  });
<script src="https://unpkg.com/[email protected]/ejs.min.js"></script>
<div data-template="/template.ejs"></div>
<div data-template="/other-template.ejs"></div>

Either way, keep in mind that JS will run after the static HTML is parsed and the DOM loads. This means the data won't appear all in one fully-formed piece as when using EJS on the server. Network errors are possible.

See also using ejs partials from express client side. If you want to mock the include function, the problem is that the fetch call is asynchronous but the include function isn't. EJS offers an include callback that seems like it offers an opportunity to pull in an external file, but it's purely synchronous and won't await any promises you return. How to work around this best depends on your use case.

Alkyne answered 31/5, 2022 at 18:13 Comment(0)
S
-1

This should work, looks like your problem was the relational operator '>' because it will never output something.

<ul>
    <% for(var i=0; i<the_list.length; i++) { %>
        <li>
            <a>
                <%= the_list[i]%>
            </a>
        </li>
    <% } %>
</ul>
Strickler answered 6/12, 2016 at 18:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.