Does EJS handle array.map(callback)? - node.js

I am passing an array of objects to an EJS template, which I would like to map into links using the regular .map() method of arrays. But for reasons I can't figure out, the callback I'm passing to map() doesn't work within EJS the way I'd expect, and I'm getting empty results.
My data is an array of objects, each with a "section" and a "name" key. This array is passed as "entries" to the template:
siteHeaders = [ { section: "home", name: "Home"},
{ section: "about", name: "About Me"},
... plus few more ]
The template looks like this, and I have it in a local variable called (surprise) template:
<% entries = entries.map(function(elem) { -%>
<% return -%>
<a href="/<%= elem.section %>">
<%= elem.name %>
</a>
<% } ) -%>
<p><%- entries.join(" | ") %></p>
The result of this template, when I call require('ejs').render(template, {entries: siteHeaders}) is:
<p> | | | | </p>
What I don't get is why this doesn't work in an EJS template, when the corresponding map call works just fine in REPL:
> siteHeaders.map(function(e){ return '' + e.name + '' })
[ 'Home',
'About Me',
'Portfolio',
'Blog',
'Contact' ]
>
Any clues?

This code should work:
<% entries = entries.map(function(elem) {
return '' + elem.name + '';
}) -%>
<p><%- entries.join(" | ") %></p>
You can't use simple html inside of functions. It's possible only in loops and conditions.

It's not as clean as join(' | '), but if you don't want to concatenate, you can do this:
<% entries.forEach(function(entry, i, entries){ %>
<%= entry.name %>
<%= i == entries.length-1 ? ' | ' : '' %>
<% } %>

I didn't manage to use map in EJS, but I found a way that at least for me is easy too
<ul>
<% for (item of toDoList) { %>
<li> <%= item %> </li>
<% } %>
</ul>

Related

Is there a way to Include / Render a variable in EJS?

Is there a way to have the contents of an include come from a variable instead of a from a file?
I need to get the contents of the include from a database instead of a file, I would like to do something like this:
Contents of example.ejs:
<h1>Example EJS template<h1>
<p><%- include(data.includeCode) %><p>
Nodejs / Express with EJS code:
let includeCode = `<% if (data.dSwitch === 1) { %> 1 detected <% } else { %> Something else <% } %>`
let ejsdata = {...{includeCode: includeCode}, ...{dSwitch: 1}};
res.render("example.ejs",{data:ejsdata});
What I would like to see is the following output:
<h1>Example EJS template</h1>
<p> 1 detected </p>
I tried using <%- data.includeCode %> but that outputs as
<h1>Example EJS template</h1>
<p> <% if (data.dSwitch === 1) { %> 1 detected <% } else { %> Something else <% } %> </p>
The solution is to call ejs.render from the ejs template:
Contents of example.ejs:
<h1>Example EJS template<h1>
<p><%- ejs.render(data.includeCode,{data:data}) %><p>
NodeJS / Express with EJS code:
import ejs from "ejs";
globalThis.ejs = ejs; // make sure ejs is available in the global context
let includeCode = `<% if (data.dSwitch === 1) { %> 1 detected <% } else { %> Something else <% } %>`
let ejsdata = {...{includeCode: includeCode}, ...{dSwitch: 1}};
res.render("example.ejs",{data:ejsdata});
Resulting Output:
<h1>Example EJS template</h1>
<p> 1 detected </p>

ejs array in an object

Here is the code in nodejs(express):
response.redirect('file',{test: [{name:'sarah',arr:[1,2,3]},{name:'beck',arr: [2,3,4]}]
Now I want to access the 'arr' for every name in the array of objects.In ejs file:
<% test.forEach(index,item){ %>
<p><%= item.arr %></p>
<% }) %>
This is printed as 1,2,3 and 2,3,4 but I want the answer to be [1,2,3] and [2,3,4].Can someone help?
You can use a for .. of loop to iterate over all entries of test and then use JSON.stringify on the entries' arr property:
<% for(const testData of test){ %>
<%= JSON.stringify(testData.arr) %>
<% } %>

ejs + nodejs - compare two variables doesn't work

I pass two variables during rendering in nodejs. Let's say they're templates & treeInfo.
In template.ejs I have.
<% for(var i=0; i<templates.length; i++) {%>
<%= templates[i]._id %> = <%= treeInfo.owner[0] %><br>
<% if (templates[i]._id == treeInfo.owner) { %>
ok
<% } %>
<% } %>
So actually if == doesn't work as expected. Here's the output.
59519779f36d284c166f9bea = 5941789e36593262bed9256b
5941789e36593262bed9256b = 5941789e36593262bed9256b
So it doesn't compare them the right way. If I just replace treeInfo.owner with something like '5941789e36593262bed9256b', it does work fine.
I assume ejs doesn't support variables comparation?
Thanks
Variable type can't understand.That's why toString() is required.
<% for(var i=0; i<templates.length; i++) {%>
<%= templates[i]._id %> = <%= treeInfo.owner[0] %><br>
<% if (templates[i]._id.toString() == treeInfo.owner.toString()) { %>
__append('Hello World');
<% } %>
To compare two objects / variables in EJS, as well as in nodejs
if(user1.equals(user2))
{
console.log("Both are equal");
}

How to pass custom variable to partial in hexo?

I want to create an alternating layout using the static site generator hexo – the text of every second post on an overview page should be on the right.
The partial I’m using needs to pass on a custom variable like textOrientation = "left" to the partial function.
<%site.tags.findOne({name: 'featured'}).posts.sort('date', -1).limit(5).each(function(post, index) {%>
<%- partial('_partial/project-excerpt', {item: post}) %>
<% })%>
The template project_excerp.ejs then needs to generate the according classes bases on the passed variable.
My template (project_excerp.ejs):
<a class="???" href="/project/<%= item.slug %>"><%= item.title %></a>
So the questions are:
How can I extend the post variable with my own variable textOrientation and
How can I use an if then else in my project_excerp.ejs-template?
Okay, found a solution myself.
index.js:
<%site.tags.findOne({name: 'featured'}).posts.sort('date', -1).limit(5).each(function(post, index) {%>
<% if(index % 2 === 0) { %>
<% post.textOrientation = "left"; %>
<% } else { %>
<% post.textOrientation = "right"; %>
<% } %>
<%- partial('_partial/project-excerpt', {item: post}) %>
project_excerp.ejs:
<% var projectInfoClass = "ta-right"; %>
<% if(item.textOrientation === "right") {%>
<% projectInfoClass = "ta-left"; %>
<% } %>
<a class="<%= projectInfoClass %>" href="/project/<%= item.slug %>"><%= item.title %></a>
Also helpful to read the EJS-docs.

How to get all locals in Express view

I am trying to figure out a way to get access to all the app.locals in the views. In my app.js I have
app.locals.title = "Title"
app.locals.page = "Page 1"
This is good, and in the views, I can access each with
<%= title %>
<%= page %>
However, is there a way (without doing something like app.locals.xyz.title) to get all the locals in the view so I can do something like this:
<% for(var i=0; i < locals.length; i ++ ){ %>
<li><%= locals[i] %></li>
<% } %>
You have to explitly pass it:
res.locals.locals = res.locals;
Then in the view:
<%= locals %>

Resources