Pass variable to EJS include - node.js

I have a global header used in a couple of places and I was trying to define its location in a variable that could be passed when rendering a template.
Something like:
var headerLocation = 'some/location/header.ejs';
res.render( viewDir + '/index', {
header: headerLocation
} );
And in a template file:
<% include header %>
header being the value passed in with the render.
It doesn't seem to be possible but maybe I missed something so thought I'd ask here.
EDIT:
This is mentioned in comments on answers below but to summarize, this is now available in version 2 of EJS.
See here: https://github.com/mde/ejs#includes
And related discussion here: https://github.com/tj/ejs/issues/93

Here is some demo code that can accomplish dynamic includes.
View
<div flex class="main-container">
<%- include(page) %>
</div>
Router
router.get('/', function (req, res, next) {
res.render('pages/index', {
page: 'home'
});
});

This feature has been added: if it is not path (file not found), it is evaluated as variable name. https://github.com/visionmedia/ejs/pull/156

Even though its an old question, answering it for others sake.
As per the github documentation, it seems EJS has no notion of blocks, only compile-time include. Since its compile time include, you need to hardcode the location.
So you are left with passing some flags and doing if checks in the header or parse the header as html and pass it to all the templates...

Old subject, but it may help someone.
The great thing about EJS is that it is just Javascript. So something like the following should work:
<%
const fs = require('fs');
const content = fs.readFileSync(partial);
%>
<%- content %>
Hope it helps.

Related

Cascading Template Use in mustache-express?

My intent is to define a page_layout template and a form template as well. Within the route handler i was going to parse the inner (form) template and inject into the greater layout than return.
After several plot twists i'm able to use the response.app to get access to my mustache template engine and parse a view, but the HTML is getting escaped :(
let router = require('express').Router();
let mustache = require('mustache');
router.get('/plant', function(request,response) {
response.app.render('plantForm', {pageTitle: "Plant"},function (err, html) {
response.render ('layout', { pageContent : html});
});
});
yields the outer template with escaped html where I expect content:
<div class="form-group">
<label for="plantNameInput">Plant Type</label>
Perhaps I'm misusing the technology?
From the Mustache documentation:
All variables are HTML escaped by default. If you want to return unescaped HTML, use the triple mustache: {{{name}}}.

handlebars - add content to head of view from partial

I am using express-handlebars in my project and have the following problem:
Question
I want to be able to add <script> oder such tags to my overall views head from a partial that is called inside the view.
Example:
The view
{{#layout/master}}
{{#*inline "head-block"}}
<script src="some/source/of/script">
{{/inline}}
...
{{>myPartial}}
{{/layout/master}}
The view is extending another partial (layouts/master) that I use as a layout. It adds its content to that ones head block through the inline partial notation, which works fine
the Partial "myPartial
<script src="another/script/src/bla"></script>
<h1> HELLO </h1>
Now I would like that particular script tag in there to be added to my views head-block. I tried going via #root notation but can only reference context there. Not change anything.
I know I could use jquery or similar to just add the content by referencing the documents head and such. But I wanted to know if this is possible at all via Handlebars.
I do doubt it is in any way. But if you have any ideas or suggestions, please do send them my way! Many thanks!!!
UPDATE
This wont work if you have more than one thing injected into your layout / view. Since this happens when the browser loads the page, it creates some kind of raceconditions where the helpers has to collect the things that have to be injected into the parent file. If its not quick enough, the DOMTree will be built before the helper resolves. So all in all, this solution is NOT what I hoped for. I will research more and try to find a better one...
Here is how I did it. Thanks to Marcel Wasilewski who commented on the post and pointed me to the right thing!
I used the handlebars-extend-block helper. I did not install the package, as it is not compatible with express-handlebars directly (Disclaimer: There is one package that says it is, but it only threw errors for me)
So I just used his helpers that he defines, copied them from the github (I am of course linking to his repo and crediting him!) like so:
var helpers = function() {
// ALL CREDIT FOR THIS CODE GOES TO:
// https://www.npmjs.com/package/handlebars-extend-block
// https://github.com/defunctzombie/handlebars-extend-block
var blocks = Object.create(null);
return {
extend: function (name,context) {
var block = blocks[name];
if (!block) {
block = blocks[name] = [];
}
block.push(context.fn(this));
},
block: function (name) {
var val = (blocks[name] || []).join('\n');
// clear the block
blocks[name] = [];
return val;
}
}
};
module.exports.helpers = helpers;
I then required them into my express handlebars instance like so:
let hbsInstance = exphbs.create({
extname: 'hbs',
helpers: require('../folder/toHelpers/helpersFile').helpers() ,
partialsDir: partialDirs
});
Went into my central layout/master file that`is extended by my view Partial and added this to its <head> section
{{{block 'layout-partial-hook'}}}
(The triple braces are required because the content is HTML. Else handlebars wont recognize that)
Then in the partial itself I added things like so:
{{#extend "layout-partial-hook"}}
<link rel="stylesheet" href="/css/index.css"/>
{{/extend}}
And that did the trick! Thanks!!!

how to include dynamic html file in html page in node.js?

I have set value in variable {action:'page1.html'} after render this value in index.html layout then I want to use include dynamically page but not working.
router.get('/', function(req, res, next) {
console.log(req.query.action);
res.render('users/index', {action:'page1.html'});
});
**index.html**
<% include+"./"+action %>
Please suggest..
First of all you must have versione 2.x of EJS, then you need to include it as:
<%- include(action) %>
Ref: https://github.com/mde/ejs#includes

node/express retrieving html templates from mongodb

Hi I'm new to node and I'm trying to make a simple blog.
I want to route /pages/:post to search a database for "post" and return an html template file that I can use as a partial.
var posts = {
'myarticle': {
template: partial1.html
}
};
var findPost = function (post, callback) {
if (!posts[post])
return callback(new Error(
'No post matching '
+ post
)
);
return callback(null, posts[post]);
};
app.get('/pages/:post', function(request, response) {
var post = request.params.post;
findPost(post, function(error, post) {
if (error) return;
return response.render('posttemplate', post);
});
});
and posttemplate is a template file like ejs (i'm not comfortable with Jade as of yet) that looks like
<html>
<% include ../partials/header %>
<body>
<% include /thereturnedpost %>
</body>
</html>
Is this possible? I've looked through documentation/tutorials but none of them are clear.
I'l answer with codes examples from my current project
<body>
<%- body %>
<script type="text/javascript" src="/vendors.js"></script>
<script type="text/javascript" src="/main.js"></script>
</body>
and
res.render('index', {
body: content
});
So your example is almost correct. After fetching the data from the database you send it to render function (in my case I render content ).
It is for the case when you keep in the db html string.
Also I can recommend you to use markdown (e.g. markdown-js) for the blog posts. It may be simpler to write and edit well formatted content, and less information to keep (comparing to html).
As per your coding, your blog post contents are static and are in template htmls.
If you get the data from db, you should modify your code such that the template file is with the HTML markup/ejs variables and send the data values separately.
This can be accomplished through ejs.renderFile method...https://www.npmjs.com/package/ejs2
Hope it will help you...

Using interpolation within Node.js EJS includes

My Express app is using EJS, and my views directory looks like this:
./views
./contents
home.ejs
./includes
header.ejs
footer.ejs
layout.ejs
I'm trying to load home.ejs in my layout.ejs view conditionally based on a local variable named contents in my routes/index.js. That file looks like this:
/*
* GET home page.
*/
exports.index = function(req, res){
res.render('index', { title: 'Home', contents: 'home.ejs' });
};
Ideally I could simply write (in layout.ejs):
<% include '/contents' + contents %>
where the trailing "contents" is the local variable which contains the relative path to the body text to load.
But alas, it appears EJS always interprets the text following an include directive literally, and there is no chance for any interpolation magic to happen.
I've also tried to no avail:
<% function yieldContent(contents){ %>
<% var contentPath = 'contents/' + contents; %>
<% include contentPath %>
<% }; %>
<% loadContent(); %>
Does anyone have a creative solution for conditionally including a view based on a variable passed in routes?
I think there is no way to do this kind of dynamic includes in EJS. It might break the separation of business logic and view.
The solution can be to rendering the subtemplate in the controller, and passing its content to the layout.
For rendering subtemplate in the controller use something like this:
var ejs = require('ejs'),
, fs = require('fs')
, home = ejs.render(fs.readFileSync("contents/home.ejs", "utf-8"))
In the version 2 of EJS, the include function does it well. With it, includes are inserted at runtime so variables can be used as pathnames.
In this case, the solution may be :
<%- include('contents/' + contents) %>
The function can also have another argument if necessary :
<%- include('mypathname', {foo:"bar"}) %>
The pathname has to be relative to the template which calls the function.
Currently this hasn't been implemented into ejs but, there is this discussion and pull request that offers the functionality.
https://github.com/visionmedia/ejs/issues/93
in your render function you can include fs.readFileSync and __dirname.
Render your page with options like this
res.render('pages/'+req.file,{file_get_contents:fs.readFileSync,__dirname:__dirname});
Then you can use it in your .ejs page like this. This remains in server side.
<% var products=JSON.parse(file_get_contents(__dirname+'/web/data/products.json','utf8')) %>
You can print the data on client HTML like this.
<%- JSON.stringify(products)%>
Note : Using this method means you have fs included somewhere at the top of your script.
var fs = require('fs')

Resources