Is there a way to Include / Render a variable in EJS? - node.js

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>

Related

In Nodejs, how can i check params in Ejs render

I want to display another view if there is :id in Semantic URL
app.get(['/topic', '/topic/:id'], function(req, res){
id = req.params.id
if(id){
res.render('view',{topics:rows, topic:row[0]}
}else{
res.render('view', {topics:rows}
}
}
and view.ejs
for(var i = 0; i < topics.length; i++){
<li><%= topics[0].title%></li>
}
<% if(topic) {%>
<%= topic.description %>
<%} else {%>
<h1>Welcome</h1>
<% } %>
it did works correctly when i connect to localhost:3000/topic/1
but when i connect to localhost:3000/topic
console said topic is not defined
what is the problem?
When i use
res.render('views', {topics:rows, topic:''}
it did works correctly
Do i have to use like this?
In view.ejs it is best to check the if condition as follows. Not only in ejs but in entire javascript world
<% if(typeof topic !== 'undefined') {%>
<%= topic.description %>
<%} else {%>
<h1>Welcome</h1>
<% } %>
And you can use also use res.render('views', {topics:rows, topic:''}

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.

Sails.js flash message for user registration

I am following Sail.js tutorial from http://irlnathan.github.io/sailscasts/blog/2013/08/27/building-a-sails-application-ep4-handling-validation-errors-with-a-flash-message/
However I ran into a small problem. In the tutorial the author uses registration files inside his user folder and assigns routes in the user controller. He then sends validation errors using flash to the user.
However in my project, the registration files lies in the root folder and I assign the routes from the routes.js file like so
module.exports.routes = {
'/': {
view: 'index'
},
'/register': {
view: 'register'
}
};
Now the problem is to using flash to show users the validation errors while registration. I have used the following in the user controller (create) and it doesn't seem to work
if (err){
err.success = 0;
console.log(err);
req.session.flash = {
err: err
}
req.flash('error', req.session.flash);
return res.redirect('/register');
}
Any suggestions?
Sails.js version < 0.10.x based on his other thread output here
EDIT: See the Sails documentation here for more information - basically, since you're using static routing, no policies are applied before rendering the view, hence the flash policy isn't working. I'd recommend adding a register action in your UserController and just calling res.view() from there. There's also a StackOverflow post that discusses this, if you want more information.
I do have an alternative I developed for my own project that you can try out (also requires non-static routing).
In your api/policies folder, create a policy flash.js:
// flash.js policy
module.exports = function(req, res, next) {
res.locals.messages = { success: [], error: [], warning: [] };
if(!req.session.messages) {
req.session.messages = { success: [], error: [], warning: [] };
return next();
}
res.locals.messages = _.clone(req.session.messages);
// Clear flash
req.session.messages = { success: [], error: [], warning: [] };
return next();
};
This policy allows for three different flash types: success, warning, and error. It'll create an empty dictionary for each session and clear it on page loads.
I created a service FlashService.js in api/services to more easily flash messages:
// FlashService.js
module.exports = {
success: function(req, message) {
req.session.messages['success'].push(message);
},
warning: function(req, message) {
req.session.messages['warning'].push(message);
},
error: function(req, message) {
req.session.messages['error'].push(message);
}
}
Then, in your config/policies.js config file, make sure to assign the flash policy to the controller actions that you want to use flash with:
// config/policies.js
module.exports.policies = {
'*': [true, 'flash'],
'UserController': {
'register': ['flash'],
// any future actions that want flash
},
'AnotherController': {
'someAction': ['flash', 'somePolicy'],
}
}
To flash a message in your register action, just use FlashService.success(req, message). Alternatively, you can use FlashService.error and FlashService.warning, depending on how your UI styling works and how much you want to differentiate your flash messages.
In your view, you can put something like this:
<% if (messages && messages['error'].length > 0) { %>
<div class="alert alert-danger">
<% messages['error'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['warning'].length > 0) { %>
<div class="alert alert-warning">
<% messages['warning'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['success'].length > 0) { %>
<div class="alert alert-success">
<% messages['success'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
Of course, you'll have to change the div classes to whatever classes are relevant to your UI.
In a normal mvc like rails or cakephp handling flash is something like this: $this->Flash->error('An Error Occurred Message'); then displayed it in an element.
In sails it should be simple as that.
In Controller: req.flash('error', 'An Error Occurred Message');
In View Partial: flash.ejs
<% if (req.session.flash) { %>
<% if (req.session.flash.success) { %>
<div data-alert class="alert-box success radius">
<%= req.flash('success') %>
×
</div>
<% } %>
<% if (req.session.flash.warning) { %>
<div data-alert class="alert-box warning radius">
<%= req.flash('warning') %>
×
</div>
<% } %>
<% if (req.session.flash.error) { %>
<div data-alert class="alert-box alert round">
<%= req.flash('error') %>
×
</div>
<% } %>
<% } %>
On Layout:
<%- partial('partials/flash') %>
Handling Error Message per field should be part in a Validation and Html Helper. (Like for example: https://gist.github.com/mikermcneil/8366092)
You can add this export:
module.exports = function flash(req, res, next) {
if (!req.session.flash) {
req.session.flash = {};
req.session.flash['success'] = [];
req.session.flash['warning'] = [];
}
next();
};
and then in the view:
<% if (req.session.flash['success'].length > 0) { %>
<div class="alert alert-success">
<%= req.session.flash['success'].pop() %>
</div>
<% } %>
<% if (req.session.flash['warning'].length > 0) { %>
<div class="alert alert-warning">
<%= req.session.flash['warning'].pop() %>
</div>
<% } %>
You can create messages in the controller like this:
req.session.flash['success'].push('Updated successfully');

Does EJS handle array.map(callback)?

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>

Resources