Nodejs + Handlebars - Pull current url path and pass to Helper - node.js

I am trying to create a meta tag handlebars helper that grabs the url pathname and uses that value for a switch statement which will then return the string to my meta tag in HTML head, but I am not sure the best way to currently grab the url path for my switch statement. I tried window.location.pathname, but get an error that window is not defined. I know that the path module requires you to pass something to it to parse out, but I'm not sure what the best value would be for that. Can anyone help me?
Here is my handlebars helper file:
var path = require('path');
var metaHelpers = function(hbs) {
hbs.registerHelper('metaTitle', function(){
var urlPath = path.dirname();
console.log(urlPath);
switch(urlPath) {
case "/": {
return 'Index Test'
}
break;
case "/login": {
return 'Login Test'
}
break;
default: {
return 'No Meta Tag'
}
}
});
};
module.exports = metaHelpers;

Since your templates are executed on the server there is no window object to query. Instead you must get the URL path from the current Request object. Fortunately, in Express the Request object has a path property.
However in order to implement your solution as a Handlebars helper you would need to find a way to pass the Request object (or at least its .path) to the helper from the view. I think a better solution would be to execute your helper logic and construct the title before the response is rendered.
Express has the concept of middleware which are functions that can modify the Request and Response objects on a per request basis. We could write a middleware function to construct the title for each request and then add it to the locals property of the Response object. Properties of the res.locals object will be available to the view(s) rendered for the current response.
app.use(function (req, res, next) {
switch (req.path) {
case '/':
res.locals.title = 'Index Test';
break;
case '/login':
res.locals.title = 'Login Test';
break;
default:
res.locals.title = 'No Meta Tag';
}
next();
});
In our layout, we can access the title property as we would any other property of our view model.
<title>{{title}}</title>
For reference, this answer provides a similar solution, except that it assigns the req object to the res.locals.

Related

Send variables to all routes in exressjs

Using nodejs/expressjs to build the APIs for my web app, I want to send some variables to all APIs, such as site title and description and so on.
I stumbled upon the old solution using dynamicHelper() which is no longer in use. What is the new approach to do so?
Easiest thing is to just put in some middleware that attaches it to the response object as locals (those will show up in your views automatically). Something like:
app.use(function(req,res,next) {
res.locals = {
title : 'your title',
description : 'your description'
};
return next();
});
** EDIT to account for what the API endpoints have to do
Since each endpoint is likely responsible for its own object, you would also do something like:
app.get('/whatever', function(req,res){
var json = {};
// do whatever to build your json
json.metadata = res.locals; // or whatever the common stuff is
res.send(json);
}
This keeps all your 'common' stuff in one part of the json response.
Since you mention you are not using any view engine in expressjs, I am assuming you are just relying on angularJS to do the client side redering. You can pass those server side data to the http header, and then read them from the client side. To do that, in your router, you can do this,
app.use(function(req,res,next) {
res.set({
'title': 'my title',
'description': '123'
});
next();
});
Then in your angularJS app, you can read them from the http header.
You should try interceptors in your front end side(angular js) to send multiple variable with each request api.
In following code i am sending title and description in headers.
module.factory('varInfoInterceptors', function($q) {
var sendInfoInjector = {
request: function(config) {
config.headers['x-headers-title'] = 'Test title';
config.headers['x-headers-desc'] = 'This is test site';
return config;
}
};
return sendInfoInjector;
});
module.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('varInfoInterceptors');
}]);
You can get these values in server side(nodejs/expressjs) by just calling req.headers express routes
Thanks,
Dinesh

How to save Backbone JSONP model to MongoDB using Node/Express?

I need a way to parse my JSONP object on server side to save it, due to cross domain origin issue I have shifted my way of communication from JSON to JSONP but not finding any suitable way to parse JSONP on server side to save it to the database.
Following is the Model,
define(['backbone'],function(Backbone){
'use strict';
return Backbone.Model.extend({
url:"http://crossdomain:9847/page",
defaults: {
type:'text',
position:0,
align:'left',
text:{"en":""},
color:"#000",
weight:'normal',
size:"14px",
font:"Verdana",
pageid:'askdkasdkgaskdgks'
},
idAttribute:'_id',
sync: function(method, collections, options) {
options.dataType = "jsonp";
return Backbone.sync(method, collections, options);
}
});
});
Express Server,
var express = require('/root/node_modules/express');
var page = require('./routes/page.js');
var app = express();
app.configure(function () {
app.use(express.json());
app.use(express.urlencoded());
app.set("jsonp callback", true);
})
app.get('/page', page.updatePage);
app.listen(9847);
exports.updatePage = function(req, res) {
console.log(req.query);
// Here how I can parse the req is my problem
// so I can save object to database?
}
URL is generating like,
http://crossdomain:9847/page?callback=jQuery203010156283635587138_1384408493698&{%22text%22:{%22en%22:%22Text%22},%22type%22:%22text%22,%22position%22:0,%22align%22:%22left%22,%22color%22:%22#000%22,%22weight%22:%22normal%22,%22size%22:%2214px%22,%22font%22:%22Verdana%22,%22pageid%22:%22askdkasdkgaskdgks%22}&_=1384408493700
and I am able to receive,
{ callback: 'jQuery203010156283635587138_1384408493698',
'{"text":{"en":"Text"},"type":"text","position":0,"align":"left","color":"': '' }
Now how can I parse this ? I can get callback from callback parameter, but how to get actual data ?
You can't parse the result because it's not valid JSON. Your problem is probably in this line:
app.set("jsonp callback", true);
This is where you set the JSONP callback variable, for example changing it from the default of callback to instead be callbackVariable.
Just comment out that line, and the JSONP you get back will hopefully be parseable. Or, you might also have to fix how Backbone is constructing the JSONP URL. If you instead used a URL like http://crossdomain:9847/page?callback=jQuery203010156283635587138_1384408493698&type=text&position=0&align=left&color=%23000&weight=normal&size=14px&font=Verdana&pageid=askdkasdkgaskdgks I believe it would work. Backbone seems to be adding additional encoding into the values in the URL, which makes parsing harder.
Finally, if you need help easily picking specific values out of a (valid) JSON string that has been parsed into a Javascript object, take a look at the many useful function in lodash.

How to get req and res object of Expreejs in .ejs file

I am trying to use Express js with .ejs views.
I want to redirect my page to some another page on any event let say "onCancelEvent"
As per Express js documentation,I can do this by using res.redirect("/home");
But I am not able to get res object in my ejs file.
Can anyone Please tell me how to access req and res object in .ejs file
Please help.
Thanks
Short Answer
If you want to access the "req/res" in the EJS templates, you can either pass the req/res object to the res.render() in your controller function (the middleware specific for this request):
res.render(viewName, { req : req, res : res /* other models */};
Or set the res.locals in some middleware serving all the requests (including this one):
res.locals.req = req;
res.locals.res = res;
Then you will be able to access the "req/res" in EJS:
<% res.redirect("http://www.stackoverflow.com"); %>
Further Discussion
However, do you really want to use res in the view template to redirect?
If the event initiates some request to the server side, it should go through the controller before the view. So you must be able to detect the condition and send redirect within the controller.
If the event only occurs at client side (browser side) without sending request to server, the redirect can be done by the client side javascript:
window.location = "http://www.stackoverflow.com";
In my opinion: You don't.
It is better to create the logic that determines the need to redirect inside some middleware that happens long before you call res.render()
It is my argument that your EJS file should contain as little logic as possible. Loops and conditionals are ok as long as they are limited. But all other logic should be placed in middleware.
function myFn( req, res, next) {
// Redirect if something has happened
if (something) {
res.redirect('someurl');
}
// Otherwise move on to the next middleware
next();
}
Or:
function myFn( req, res, next) {
var options = {
// Fill this in with your needed options
};
// Redirect if something has happened
if (something) {
res.redirect('someurl');
} else {
// Otherwise render the page
res.render('myPage', options);
}
}

Backbone.js/express.js parameters for model.save()

I'm using Backbone.js on the client and node.js on the backend, and I'm having a bit of trouble doing a 'limited' model save, as explained here : http://backbonejs.org/#Model-save
As in the example, if I do
book.save({author: "Teddy"});
how do I access the parameters of the save using express.js, i.e. the fact that I only want to save to the 'author' field? I've tried the following
req.body -> gives ALL parameters of the model being saved, I want only the 'author' field
req.query -> empty
Any help much appreciated!
As stated in Model.save documentation:
save model.save([attributes], [options])
[...] The attributes hash (as in set) should contain the attributes you'd like to change —
keys that aren't mentioned won't be altered — but, a complete
representation of the resource will be sent to the server.
You can however override the save method and provide a data attribute via the options which will be sent to the server instead of the full model representation. For example, this will only save the attributes passed to the method :
var M = Backbone.Model.extend({
save: function (attrs, options) {
options || (options = {});
options.contentType = 'application/json';
options.data = JSON.stringify(attrs);
Backbone.Model.prototype.save.call(this, attrs, options);
}
});
And a Fiddle http://jsfiddle.net/dLFgD/
As #mikebridge noted in the comments, this behavior can now be obtained by passing an attrs option. So either use
book.save(null, {
attrs: {author: "Teddy"}
});
or keep the override
var M = Backbone.Model.extend({
url: '/echo/json/',
save: function(attrs, options) {
options || (options = {});
options.attrs = attrs;
Backbone.Model.prototype.save.call(this, attrs, options);
}
});
http://jsfiddle.net/nikoshr/dLFgD/7/
You could also send a PATCH request if you're using a Backbone version that supports it (>=0.9.9) and your server understands that verb, as explained in #pkyeck's answer
with the current version of Backbone (1.1.0) you could also do a PATCH which only sends the changed attributes to the server.
If instead, you'd only like the changed attributes to be sent to the server, call model.save(attrs, {patch: true}). You'll get an HTTP PATCH request to the server with just the passed-in attributes.
taken from here: http://backbonejs.org/#Model-save
in your case this would be:
book.save("author", "Teddy", {patch: true});
The best way I have found to access POST parameters is to use the bodyParser middleware. This is referenced at this question: How to retrieve POST query parameters?
/* On startup, register the bodyParser middleware */
app.use(express.bodyParser());
/* Then, during route execution, each parameter can be accessed with req.param */
app.post('/author/:id', function(req, res) {
var author = req.param('author', null); // Second parameter is default.
...
});

How to override flash messages in express.js using dynamic helpers?

I have been working through the tutorial by Alex Young on using flash messages. According to this tutorial, one is able to override the default flash messages formatting using dynamicHelpers. Unfortunately there are no details provided on what is going on, and it is not possible to post any comments on the tutorial page to ask relevant questions.
What I fail to see is the relationship between the call to req.flash() in the file 'app.js', and the FlashMessage object exported in file 'helpers.js'. Why would a regular call to req.flash(), which is a standard function in express.js, link to this FlashMessage prototype in the first place? I can't see how this is happening when I look at the code.
At first I thought the FlashMessage object might have been provided to req.flash() by express.js, in which case we are just extending or overriding it in our helper file. The problem with this is that I cannot find any reference to FlashMessage in the source code of express.js.
I would be really grateful if someone could explain it to me.
The flash message is set in file 'apps.js' by calling:
req.flash('info', 'Document created.');
The FlashMessage obect is exported 'helpers.js':
FlashMessage.prototype = {
// Get css definition string for icon.
get icon() {
switch (this.type) {
case 'info':
return 'ui-icon-info';
case 'error':
return 'ui-icon-alert';
}
},
// Get css class for message container.
get stateClass() {
switch (this.type) {
case 'info':
return 'ui-state-highlight';
case 'error':
return 'ui-state-error';
}
},
// Returns HTML formatted message.
toHTML: function() {
return '<div class="ui-widget flash">' +
'<div class="' + this.stateClass + ' ui-corner-all">' +
'<p><span class="ui-icon ' + this.icon + '"></span>' + this.messages.join(', ') + '</p>' +
'</div>' +
'</div>';
}
};
exports.dynamicHelpers = {
flashMessages: function(req, res) {
var html = '';
['error', 'info'].forEach(function(type) {
var messages = req.flash(type);
if (messages.length > 0) {
html += new FlashMessage(type, messages).toHTML();
}
});
return html;
}
};
In app.js file the full routing function which calls req.flash is as follows:
// Attach dynamicHelpers to app.
app.dynamicHelpers(require('./helpers.js').dynamicHelpers);
// Routing function which calls req.flash.
app.post('/documents', loadUser, function(req, res) {
// Create Document object and assign value.
console.log('Document content: %s', req.body['document']);
var document = new Document(req.body['document']);
document.save(function() {
// Redirect to another page.
req.flash('info', 'Document created.');
res.redirect('/documents');
}
});
});
There are 2 different things here:
a) req.flash() which is implemented by Express itself - not by you, you're just using that function
b) your dynamic helper:
From the Express guide:
Dynamic view helpers are simply functions which accept req, res, and
are evaluated against the Server instance before a view is rendered.
The return value of this function becomes the local variable it is
associated with.
app.dynamicHelpers({
session: function(req, res){
return req.session;
}
});
Let's "translate" that into your code:
// Attach dynamicHelpers to app.
app.dynamicHelpers(require('./helpers.js').dynamicHelpers);
That means that when you call the variable flashMessages in your view code, you'll get the html representation of those flash variables defined.
So the most important thing here is to consider you are only using req.flash(), not implementing it. You are implementing a helper that is using that function.

Resources