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