EJS: <% vs <%- --> What's the difference - node.js

The EJS documentation summarizes the difference between <% and <%- as below:
<% 'Scriptlet' tag, for control-flow, no output
<%= Outputs the value into the template (HTML escaped)
<%- Outputs the unescaped value into the template
However, I noticed that I get the same HTML output whether I use <% or <%-, as below
<%# Include header %>
<% include partials/header %> //Using <%
<h1>This is the home page</h1>
<p>Some content goes here</p>
<%# Include footer %>
<%- include partials/footer %> //using <%-
This is my header.ejs file
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" type="text/css" href="/app.css">
<title>Demo App</title>
</head>
<body>
I've already checked out the following quesion (EJS: <%= versus <%-) but it didn't explain this behaviour.

<%- and <% tags have different purposes, the first one is simply for unescaped output:
const template = '<%- user %>';
ejs.render(template, { user: 'Alice' }); // renders "Alice"
But let's say there are many users, in this case it may be required to use some flow control structure to iterate over users, this is when <% is used:
const template2 = '<% users.map(user => { %> <%- user -%> <% }) %>';
ejs.render(template, { users: ['Alice', 'Bob'] }); // renders "Alice Bob"
As you can verify, in these examples tags <%- and <% have different behavior and are not interchangeable.
The case you have described with include is pretty special. In my opinion, the expected behavior would be not to output the partials/header (with <%) as it is a simple template (not a flow control).
This is how it works with modern include syntax, i.e if you try to include the header using:
<% include("partials/header") %>
instead of
<% include partials/header %>
you will see that there is no output.
As for the legacy include syntax, it appears that ejs treats it equally inside <% and <%- tags. Should you want to investigate this matter further, inspecting the library source code may be helpful: https://github.com/mde/ejs/blob/master/lib/ejs.js#L713
Or you can simply prefer the new include syntax which appears to provide more consistent behavior.

Related

Ejs doesn't render ejs file

I want to make a queue system for to create pdf file. I created a node server and used Express framework. Also I used rabbitmq for the queue system. I set view engine ejs
app.set('view engine', 'ejs');
app.use(Express.static(__dirname + "/views"));
My folder structure is
consumers
--consumer_report.js
views
report.ejs
report
--environment.ejs
--consultans.ejs
--map.ejs
When a user wants to create a pdf, I redirect it to the queue. The queue is calculating some datas. After calculating I'm using render ejs file.
createReport(msg.user, msg.reportID, msg.type, msg.packetName, (err, data) => {
data.packet = msg.packetName;
let dirUrl = __dirname + "/../views/report.ejs";
let opts = {
async: true
}
ejs.renderFile(dirUrl, data, opts, (err, html) => {
if (err) return console.error(err);
console.log("html", html);
});
});
Report Ejs
<!DOCTYPE html>
<html lang="en" style="zoom:0.75;">
<head>
<meta charset="UTF-8">
<title>Report</title>
<link rel="stylesheet" href="<%= host %>/css/rp.css">
<script src="<%= host %>/js/jquery3.2.1.min.js"></script>
<script src="<%= host %>/js/highcharts.js"></script>
<script src="<%= host %>/js/rp.js"></script>
</head>
<body>
<% for(let i of index){brackets = i.i_page; title= i.name %>
<% if(brackets=="environment") {%> <%- include report/environment.ejs %> <% } %>
<% if(brackets=="consultant") {%> <%- include report/consultants.ejs %> <% } %>
<% if(brackets=="map") {%> <%- include report/map.ejs %> <% } %>
<% } %>
</body>
</html>
When render the ejs file I get this error
Error SyntaxError: missing ) after argument list in /home/aaa/Desktop/projects/report/consumers/../views/report.ejs while compiling ejs
I couldn't find the error. Where is my mistake?
you need to change include syntax to include(path), as per docs:
NOTE: Include preprocessor directives (<% include user/show %>) are
not supported in v3.0+.
https://github.com/mde/ejs
try this:
<!DOCTYPE html>
<html lang="en" style="zoom:0.75;">
<head>
<meta charset="UTF-8">
<title>Report</title>
<link rel="stylesheet" href="<%= host %>/css/rp.css">
<script src="<%= host %>/js/jquery3.2.1.min.js"></script>
<script src="<%= host %>/js/highcharts.js"></script>
<script src="<%= host %>/js/rp.js"></script>
</head>
<body>
<% for(let i of index){brackets = i.i_page; title= i.name %>
<% if(brackets=="environment") {%> <%- include('report/environment.ejs') %> <% } %>
<% if(brackets=="consultant") {%> <%- include('report/consultants.ejs') %> <% } %>
<% if(brackets=="map") {%> <%- include('report/map.ejs') %> <% } %>
<% } %>
</body>
</html>

how to sending ejs tag data to javascript?

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var ajax_get_api_data = [1,2,3]
<% var dataArray = ajax_get_api_data %>
</script>
<% dataArray.map((data) => {%>
<div><%=data%></div>
<%}) %>
</body>
</html>
My intend of Question is for Sending script data to EJS tag
I hope to get datas by calling ajax get api on script tage then I hope to send ajaxCalling data to EJS.
I tryed like above but only to fail.
How can i fix it .
I know that it will fix simply by
<% var ajax_get_api_data = [1,2,3] %>
but it isn`t what i want. beacuse ajax_get_api_data is from script tag not ejs tag
How can i transmit script data to EJS
it seems like you are mistaken about how ejs works.
the javascript inside <% %> is ran when you render the page
the part inside script tags is ran client side
so a
<script>
var ajax_get_api_data = [1,2,3] // client side
<% var dataArray = ajax_get_api_data %> // undefined during rendering
</script>
instead you want
<script>
var ajax_get_api_data = JSON.parse('<%- JSON.stringify(dataArray) %>')
</script>
explanation :
you pass the data from server as a string (using JSON.stringify) to the client and use JSON parse to transform it back in to a array

ejs template expression in ejs template expression (not include)

My node version is v10.2.1, express version is 4.16.0, ejs version is 2.5.7.
I want to be able to change the name of the layout at a later time.
So I routing like this:
router.get('/', function(req, res, next) {
res.render('index', { title: 'my homepage' , layout: 'basic'});
});
and index.ejs
And I wrote an include in the index like this:
<% include ./layouts/basic/header.ejs %>
Now, I want to change basic to <%= layout %> but,
<% include ./layouts/<%= layout>/header.ejs %>
causes:
Could not find matching close tag for "<%".
And,
<% include ./layouts/%><%= layout %><%/header.ejs %>
causes:
Could not find the include file "./layouts/"
How can I solve it?
I am considering that you required two header views
horizontal header
vertical header
So inside views directory I have created the respective directories.
index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<%if(layouts=='horizontal'){%>
<%include ./horizontal/header%>
<%}%>
<%if(layouts=='vertical'){%>
<%include ./vertical/header%>
<%}%>
</body>
</html>
Here I have passed layouts variable from the routes. Hope it helps you to resolve the issue.

EJS include as wrapper

Can I include another .ejs file that will wrap the current content?
I want to have a general layout like this:
layout.js:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content">
<!-- I want to "inject" my code here -->
</div>
</body>
</html>
and I want to use this template from another file, like this:
content.ejs
<% inside(layout) ? { %>
content
<% } %>
Can I do something like this?
I'm currently doing this the other way around, I call layout with a parameter include_name but it's a little inconvenient. I would like to call the relevant content.ejs which includes the generic content itself. Is this possible?
Thanks,
From EJS Documentation:
EJS does not specifically support blocks, but layouts can be
implemented by including headers and footers, like so:
<%- include('header') -%>
<h1>
Title
</h1>
<p>
My page
</p>
<%- include('footer') -%>
Although some frameworks have some facilities to deal with problem. for example Express until version 3.x had layout support and for latest versions you could use it as a stand alone npm package: express-partials
With this package in place you define a <%- body %> region in your skeleton template (layout.ejs) and when you call your desired layout (content.ejs), express will render layout.ejs with content.ejs as <%- body %> ( unless you set {layout:false} which then it only renders content.ejs):
layout.ejs
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content">
<%- body %>
</div>
</body>
</html>
content.ejs
content
app.js
var express = require('express')
, partials = require('express-partials')
, app = express();
// load the express-partials middleware
app.use(partials());
app.get('/',function(req,res,next){
res.render('content.ejs')
// -> render layout.ejs with content.ejs as `body`.
})
app.listen(3000);

ejs 'partial is not defined'

Okay I have a mostly static homepage but I wanted to have partial views that for navigation, footer ect. I'm using ejs and it looks like this:
my controller: home.js
// Dependencies
var express = require('express');
module.exports = {
get: function(req, res) {
app.set('view engine', 'ejs');
var model = {
layout:'home',
};
res.render('home');
}
};
My views directory has nav, home and footer all .ejs
Then the actual html file stripped of text would look as following.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" >
<title>Tom Jones</title>
<!-- CSS -->
<link rel="stylesheet" href="/css/home.css" type="text/css" media="screen" >
</head>
<body>
<%- partial('nav') %>
<!--content part -->
<div id="showcontainer">
<section>
</section>
</div>
<div id="maincontainer">
<section>
</section>
</div>
</body>
</html>
The Problem
When ever I test it out I run into the error partial is not defined. I tried requiring ejs but no success.
As #Pickels said, Partial was removed in 3.x. However, the most recent version of EJS provides a mechanism for including "partials", called "include":
https://github.com/visionmedia/ejs#includes
Includes are relative to the template with the include statement, for example if you have "./views/users.ejs" and "./views/user/show.ejs" you would use <% include user/show %>. The included file(s) are literally included into the template, no IO is performed after compilation, thus local variables are available to these included templates.
The following will work as a replacement for your old partial() function. You'll need to make tweaks elsewhere to support Express 3.x completely, but for the most part this seems to work well (better actually - less code and more performant).
<% include nav.ejs %> <!-- replaces your old <%- partial('nav') %> -->
Now in ejs 3.1.x
<% include('relative_filepath'); %>
Must be replaced by
<%- include('relative_filepath'); %>
Partial was removed in 3.x. It's now up to the templating engine to provide partials.

Resources