Render template to variable in expressjs - node.js

Is there a way to render template to a variable instead to output?
res.render('list.ejs', {
posts: posts
});
something like this
var list = render('list.ejs', {
posts: posts
});

The easiest way to do that is to pass a callback to res.render, in your example:
res.render('list.ejs', {posts: posts}, function(err, list){
//
});
But if you want to render partial templates in order to include them in another template you definitely should have a look at view partials.

I am quite a newbie on express.js, anyway I am not sure you can access the rendered string that way, although if you look at express' "view.js" source on github (here) you see that it's accepting a callback as second argument, if that may help: you may access the rendered string there.
Otherwise, I think it's quite easy to patch the code to add a method returning the rendered string without sending it: on line #399 you have the very call that gives the string you are looking for.

This wasn't the question originally asked, but based on comments from the OP and others, it seems like the goal is to render a partial via json (jsonp), which is something I just had to do.
It's pretty easy:
app.get('/header', function (req, res)
{
res.render('partials/header', { session: req.session, layout: null }, function (err, output)
{
res.jsonp({ html: output });
});
});
Note: In my case, the header partial required the session, and my template library (express-hbs) needed layout: null to render the partial without using the default layout.
You can then call this from Javascript code in the client like any other JSONP endpoint.

Related

Fastify point-of-view local variables

I am switching from Express.js to Fastify. I need to do it quickly, so using only API is impossible yet. Haven't written React app.
My problem is: I am using point-of-view and I don't know how to pass local variable to all requests. In express there something like
app.use(function (req, res, next)
{
res.local.new_notifications = 50;
})
and I can get it in template engine on every page like
<%= new_notifications %>
Is there something like this in Fastify + point-of-view?
You can, I am not sure if it is a new implementation or not since you asked a solution more than 1 year ago, this is for future viewers.
You simple have to add a .locals object and attach to it anything you want available with your engine.
This is from the documentation:
If you want to provide data, which will be depended on by a request and available in all views, you have to add property locals to reply object, like in the example below:
fastify.addHook('preHandler', function (request, reply, done) {
reply.locals = {
text: getTextFromRequest(request) // it will be available in all views
}
done()
})
Be sure to create the object with the assignment {} since locals its not defined!

Node.js and Express - Regarding rendering pages in a directory with one call

Hopefully I'm not duplicating a question here, but I looked around and didn't see anything that addressed this specific issue.
I'm constructing some pages by creating a bunch of unique content blocks that I store in a folder - things like this:
h4 Content Example
p.description.
Lorem ipsum dolor sample text etc etc
in, say, pages\contentBlocks\Example\contentExample.jade.
These content blocks are added to a mixin (pages\mixins.jade) that looks like this:
mixin contentBlock(title)
.content-block(id=title.toLowerCase())
h3 #{title}
if block
.content
block
and I call this mixin within a larger page (pages\portfolio.jade) like this:
.subsection
//- Expects a list of objects of the format { title, html }
each val in pageBlocks
+contentBlock(val.title)
!= val.html
(the intention is to later enhance this with other variables that can be passed down into the mixin for in-place formatting, like background images)
I would like to make use of the ability of Express to automatically render all of the pages in a directory in a single call. Like so (index.js):
var pageBlocks = [];
res.render('pages/contentBlocks/Example/*', function (err, html) {
if (err) throw err;
pageBlocks.push({ html: html });
});
However, I'd also like to be able to reference the title of the file, for example for setting the class of the block that I put it in. Ideally, it would look something like this:
res.render('pages/contentBlocks/Example/*', function (err, html) {
if (err) throw err;
pageBlocks.push({ title:functionToGetFileName(), html: html });
});
Is there any way to do this without manually iterating through the files in that directory?
Or, since this method seems needlessly convoluted (given the presence of mixins, includes, extends, and partials, it feels like I ought to be able to do this without extra render calls), is there a better way to do this?
Edit: I've done a bit more looking around and code testing, and now I'm unsure. Can Express implicitly render all of the files in a directory? Based on this question, I assumed it could, but I might have misread. The formatting of res.render('path/to/directory/', callback) doesn't seem to be working - I get the following error:
Error: Failed to lookup view "path/to/directory/" in views directory "/path/to/app/views"
For a temporary solution, I am using the following pattern:
exampleBlocks = [];
eb_dir = "pages/contentBlocks/Example";
fs.readdirSync("./views/" + eb_dir).forEach(function(file) {
res.render(eb_dir + "/" + file, function (err, html) {
if (err) throw err;
exampleBlocks.push({title: file, html:html})
});
});
Which is concise enough for my tastes, though calling res.render() so many times rustles my jimmies. I'd rather it be a single batch render, or better yet, solved through clever use of jade structures - but I still haven't thought of a way to do the latter.
Edit:
I decided I'd rather not simply enumerate the files in the folder - I want more control of the order. Instead of using an each loop in my page, I'm simply going to call the mixin with included content, like this (pages\portfolio.jade in my previous examples):
.subsection
+contentBlock("Block 1")
include blocks/block1
+contentBlock("Block 2")
include blocks/block2
I will leave the question here, however, as others may encounter a similar issue.

node.js express.js Error handling in View

I am very new to node.js and express.js and in programming concepts. I already made a basic MVC modeled app on node and express.
My problem is how do you handle error, I got this following code:
exports.submitBloodRequest=function(kaiseki,resView,request){
var params = {
bloodCenterId:request.session.centerID,
bloodTypeId: request.body.bloodType,
requestQuantity:request.body.numberOfDonors
}
kaiseki.createObject('blood_center_request', params, function(err, res, body, success) {
if(success){
resView.redirect('/bloodRequest')
}else{
//WHAT TO DO HERE?
}
});
}
Kaiseki is just a middleware for parse.com, I don't know what to do if it got error. Usually I use ajaxForm.js to look for BadRequest then use javascript to display error message in my view.
I want my error to appear in the same page, where it is success, should I pass a json error to my view?
Or still use ajaxForm.js and instead of res.render or res.redirect I should use res.status(500)
Is there anyway to handle the error and showing it into the view. Without using any javascript to detect BadRequest?
And can a view have a optional variable? In my view If I didnt pass any value on it it gives me error like if i have #{variable} it asks for its value. Can it be made to be optional? Im using Jade Template
To respond to an XHR request with an error you can do something like return resView.status(500).send(err); which will send the err object back as JSON. If you want to render an HTML error page instead you can do return resView.status(500).locals({err: err}).render('/errorPage');
You didn't say which template engine you are using but most likely the #{} version will automatically escape HTML characters for you (turn < into <, etc) to avoid XSS attacks and rendering problems whereas !{} will render the contents of the variable directly without escaping, which is dangerous if the variable contains any user-generated content, but necessary if the variable has HTML you want rendered by the browser.

Node.js / Express - modifying response template context through request/response objects

I am using Express to serve web pages in node.js application.
Let's say I want to have a variable foo available in all views rendered by render method of response object. I know that I can define dynamic helpers for this task. However, I found them unsuitable when you need to set helper variable asynchronously like this (Mongoose example):
Thing.count(filter, function(error, thingCount) {
foo = thingCount;
}
I've tried using connect middleware approach, which suits me perfectly, however the question here is how to affect the response context. By looking into render method definition in express/lib/view.js I've found that it can be manipulated by writing into app._locals object:
function putFooIntoContext (req, res, next) {
Thing.count(filter, function(error, thingCount) {
res.app._locals.foo = thingCount;
next();
}
}
It works as intended, however, I am a bit afraid that such straightforward approach is not the best solution. Can someone give me any ideas how to affect response context by interacting only with request/response objects in proper way designed by Express developers?
Express 3.x allows for asynchronous helpers to be utilized in the form of 'app.use'. So for a simple global 'foo' variable, your code would be as follows:
app.use(req, res, next) {
Thing.count(filter, function(error, thingCount) {
res.locals.foo = thingCount;
next();
});
}
Of course the middleware option is also valid, this is just another viewpoint and saves inserting the middleware per each app.get(....)

Accessing jQuery object in JADE

During login, I save the userId in a jQuery cookie, so that I can access it by $.cookie('userId').
Now that I am trying to access it from Jade, as,
- if ($.cookie('userId') === userId)
I get an error
$ is not defined
How can I access the cookie ?
My code:
- if (($.cookie('userId')) === userId)
input(type='submit', value='Move')
else
input(type='submit', disabled, value='Move'')
You seem to misunderstand concepts. Let me give you a lecture. ;)
Basics. Cookie is stored on the client-side. So using jQuery $.cookie('userId') will retrieve it when this script is run on the client-side.
But here you use JADE templating engine on server-side. So you didn't even send the page to the client-side yet. So what cookie do you want to retrieve? There is no cookie, because you are at the server.
There's more. Running $.cookie('userId') throws the error, because you are on the server-side. There is no $ object. And there cannot be, because you cannot use jQuery on server-side (well, actually you can, but this is irrelevant at the moment). Did you define $ object in your route system? Or did you define helper $? I don't think so.
Express+JADE tutorial. Now let me give you a short tutorial. Say you have a view (in app.js)
app.get('/', function(req, res){
res.render('index.jade', { code: 500 });
});
Now in index.jade you can use code as a variable. So you can write your index.jade for example like this:
// some code
- if (code == 500)
input(type='submit', disabledm, value='Move')
- else
input(type='submit', value='Move')
// some code
and now it will work! You may also use JavaScript functions in template if you predefine them. For example (in your app.js)
app.helpers({
positive: function(no) { return no > 0; }
});
then in index.jade template you can use
- if (positive(-13))
input(type="submit")
and even combine these two concepts
- if (positive(code))
// do some stuff
To cookie or not to cookie. If you really need to use cookies (and you shouldn't except for sessions or CSRF for example) then you need to use client-side JavaScript. This means that you need to add (after jQuery)
script(src='my-path/my-script.js')
to the index.jade file and write my-script.js like this
$(document).ready(function() {
if ($.cookie('userId') == userId) {
// do something like append input to some holder
}
});
But there are several problems with that solution. First: what is userId (second part of equality)? You need to predefine it in your index.jade. For example using
script
var userId = #{ user.id };
and adding user object in your route. Second: cookies are local variables. Setting them requires additional server code which I do not thing you would like. This makes maintaining the app harder. And finally: storing a user's id in a cookie seems to be pointless. The final user don't even have to know it's own id. The server needs to know! Sessions system comes handy here.
Final note. The code you wrote shows us that you cannot distinguish between server-side and client-side JavaScript. The solution to your problem is to learn, learn and learn. Read tutorials and everything about JADE, Express and Node.js.
What #freakish said is right. You're trying server side vs. client side.
If the cookie is set you should be able to access it on server like so
app.use(express.cookieParser());
app.get('/', function(req, res){
// index is jade template
// local variable on jade template holds cookie value
// use req.cookies.userid
res.render('index', { userid: req.cookies.userid });
});

Resources