EJS include as wrapper - node.js

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);

Related

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

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.

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.

knockoutjs in express using ejs

I would like to start using knockoutjs in a web app currently based on Nodejs with express using ejs view engine.
index.ejs
<% layout('layout') -%>
<!-- Jquery Include-->
<script src="/js/jquery-2.1.0.min.js"></script>
<!-- Knockout Javascript Dependency-->
<script src="/js/knockout-3.0.0.js"></script>
<script type="text/javascript">
var viewModel = {
firstName : "Bert"
};
ko.applyBindings(viewModel);
</script>
<p>First Name: <span data-bind="text: firstName"></span></p>
It appears that knockout js is loading properly however, the data-binding is not working I simply see.
First Name:
Is there a compatibility issue loading knockout in an ejs file?
There are no compatibility issues with ejs - as far as I know - however you need to call ko.applyBindings after your DOM is loaded, from the documentation:
To activate Knockout, add the following line to a block:
ko.applyBindings(myViewModel);
You can either put the script block at the bottom of your HTML
document, or you can put it at the top and wrap the contents in a
DOM-ready handler such as jQuery’s $ function.
So you need to change your template to move the ko.applyBindings at the end:
<% layout('layout') -%>
<!-- Jquery Include-->
<script src="/js/jquery-2.1.0.min.js"></script>
<!-- Knockout Javascript Dependency-->
<script src="/js/knockout-3.0.0.js"></script>
<p>First Name: <span data-bind="text: firstName"></span></p>
<script type="text/javascript">
var viewModel = {
firstName = "Bert"
};
ko.applyBindings(viewModel);
</script>
Or use the jQuery’s $ function:
<% layout('layout') -%>
<!-- Jquery Include-->
<script src="/js/jquery-2.1.0.min.js"></script>
<!-- Knockout Javascript Dependency-->
<script src="/js/knockout-3.0.0.js"></script>
<script type="text/javascript">
var viewModel = {
firstName = "Bert"
};
$(function() {
ko.applyBindings(viewModel);
});
</script>
<p>First Name: <span data-bind="text: firstName"></span></p>

Connection-route of node.js taking wrong parameters informations

I've a problem with my node.js application.
I'm using the packages connection, connection-route, socket.io and ejs.
My application provides informations to the html page (connected via socket.io), these informations are managed by an ejs template.
When I reach a destination with a parameter, like http://localhost:5001/machine/:id2, something strange happens.
The connection route code is the following:
router.get('/machine/:mac_id', function (req, res, next) {
var mac_index = req.params.mac_id.slice(1,req.params.mac_id.length);
console.log(mac_index);
res.writeHead(200, { 'Content-Type': 'text/html' });
var str = mod_fs.readFileSync(mac_route + '/index.html', 'utf8') ;
var ret = mod_ejs.render(str, {
filename: mac_route,
title: "Machine Overview",
/* other informations */
});
res.end(ret);
}
The variable mac_route contains the path to the file index.html, which is loaded correctly.
The problem lies in the mac_index variable. On the console are printed 3 rows:
id2
unctions.js
query-1.9.1.js
The first row is obviously correct, the last 2 rows are obviously not correct, infact these are two javascript files (my file functions.js and the file for jquery jquery-1.9.1.js).
These files are included in the header of the index.html file.
HTML STRUCTURE:
header.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> <%= title %> </title>
<link rel='stylesheet' href='/style.css' type="text/css"/>
<script src="http://localhost:5001/socket.io/socket.io.js"></script>
<script language="javascript" type="text/javascript" src="functions.js"></script>
<script language="javascript" type="text/javascript" src="jquery-1.9.1.js"></script>
</head>
<body>
<div id="header">
...
</div>
<div id="page">
index.html
<% include /header.html %>
<div id="commands">
...
</div>
<div id="main">
... code of the page, manage informations received ...
</div>
<% include /footer.html %>
footer.html
<div id="footer">
...
</div>
</div> <!-- Close the "page" div opened in the header //-->
</body>
</html>
I can't find where's the mistake.
Why the file's names are taken as parameter of the req object?
The normalized URL for those files is:
http://localhost:5001/machine/functions.js
http://localhost:5001/machine/jquery-1.9.1.js
Those match your route (/machine/:mac_id), so they will be handled by it.
Try including the connect.static middleware before your routes:
app.use(connect.static(__dirname));
(this assumes your Connect app is stored in the app variable and the JS files are in the same directory as your Node script; if not, change __dirname to point to the directory where the JS files are located).

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