I am building a website using Node.js and Express. For my view engine, I'm using Vash.
In my app, I have a layout.vash file I'm using as the main layout for my app. It looks something like this:
layout.vash
<!DOCTYPE html>
<html lang="en">
<head>
<title>#model.title</title>
</head>
<body>
#html.block('content')
</body>
</html>
I then have views that look something like this:
view.vash
#html.extend('layout', function(model){
#html.block('content', function(model){
<h1>Hello</h1>
})
})
The view is referenced via Express with a route similar to this:
var router = require('express').Router();
router.get('/', function(req, res) {
var viewModel = {
title: 'Hello'
};
res.render('view', viewModel);
});
My viewModel will be unique to each view. However, there are some properties that are shared across all views. For example, for development purposes, I would like to know if the request is local or not. If I was using ASP.NET, I know I could say:
#if (Request.IsLocal) {
...
}
However, I'm not sure how to do something similar with Node and Vash. My instincts tell me that I somehow need to append a property called isLocal to my model. However, I'm not sure if that's the correct idea, or if that's even possible.
I use the approach you are considering in my personal code.
If the values you want to attach to your model are common across multiple routes, consider using a function to extend your model:
function appendRouteData(model) {
model.routes = {};
model.routes.controller = "SomeController";
model.routes.action = "index";
model.routes.parameters = { foo: "bar" };
return model;
}
...
viewModel = appendRouteData(viewModel);
In response to your comments, there are multiple ways to include such a function in your users.js.
Either expose the method as another module and require it, or, if we're going full crazy, inject a function that performs the same, but this requires some refactoring. I anticipate you doing only some of this insanity...
// routes.js
var UserRoutes = require('./users');
function appendRouteData(controllerName) {
// some crazy javascript here, might want to contemplate how this actually works first.
return function (viewModel) {
viewModel.routes.controller = controllerName;
return viewModel;
};
}
module.exports = {
users: UserRoutes(appendRouteData('users'))
};
// users.js
function UserRoutes(appendRouteData) {
var router = require('express').Router();
router.get('/users', function (req, res) {
var viewModel = {};
viewModel = appendRouterData(viewModel);
res.render('users/index', viewModel);
});
return router;
}
module.exports = UserRoutes;
Related
I am using following packages for multi-languages solutions.
var i18next = require('i18next');
var i18nFsBackend = require('i18next-node-fs-backend');
var i18nMiddleware = require('i18next-express-middleware');
Since I am using handlebar as my nodejs template engine, that's I can not use i18next t('key') directly in the HTML.
so I created a handlebar helper like following
```javascript
var i18next = require('i18next');
handlebars.registerHelper('t', function(i18n_key) {
console.log(i18next.language)// always undefined, so i18next.t(i18n_key) always return default translation.
var result = i18next.t(i18n_key);
return new handlebars.SafeString(result);
});
```
However, the problem was the function is unable to detect language changed
My Workaround
app.js
```javascript
var i18nextInitCallback = function(error, t){
handlebars.registerHelper('t', function(i18n_key) {
if(app.locals.language !== i18next.language){
i18next.changeLanguage(app.locals.language);
}
var result = i18next.t(i18n_key);
return new handlebars.SafeString(result);
});
};
```
route
```javascript
router.use(function(req, res, next){
res.locals.lng = req.language;
res.app.locals.language = req.language;
next();
});
```
as you can see that on Route I assign res.app.locals.language = req.language;
and then in the handlebar helper function, I use app.locals.language to get the current language and use i18next.changeLanguage() to change the language.
and it worked.
I would like to know if I am doing it right or not?
or if there is a better solution
Using the handle function of the middleware:
app.use(middleware.handle(i18next, {
// options
}));
res.language gets already set for you and a t function fixed to user language of that request.
see: https://github.com/i18next/i18next-express-middleware/blob/master/src/index.js#L48
check out the handlebars sample: https://github.com/i18next/i18next-express-middleware/tree/master/examples/basic-handlebars
I am making a project with node, express, and jade. I want to access content through:
/Page/foo/bar
and
/Page?Foo=foo&Bar=bar
I want the top to be an alias for the bottom.
This is the solution I have now:
server.js
// some stuff
app.get('/Page/:Foo/:Bar',function(req,res){
res.render('Page.jade', {Foo: req.params.Foo, Bar: req.params.Bar});
});
app.get('/Page',function(req,res){
res.render('Page.jade', {Foo: req.query.Foo, Bar: req.query.Bar});
});
// more stuff
Page.jade
doctype html
html
head
script var foo = "!{Foo}"; bar = "!{Bar}";
script(src="/Page.js")
// stuff
Page.js
// stuff with foo and bar, such as:
console.log(foo);
console.log(bar);
The thing I don't like about this solution is that it forces me to handle the params and query separately with express (which is almost duplicate code, but not quite close enough to reduce it), pass it to jade, which stores it in a variable for the sole purpose of having a linked javascript file use those variables.
Normally just using query strings I would only have to touch it in Page.js. Is there a way to set up express to effectively interpret the first URL as a query string, like the second URL, so the jade file doesn't have to touch the variables?
Option 1: If you just want to reduce code redundancy, maybe you could save your controllers in an external file, so you will end up with something like this:
controllers/fooBarController.js:
exports.fooBarQueryOrParams = function(req, res) {
res.render('Page.jade', {
Foo: req.params.Foo || req.query.Foo,
Bar: req.params.Bar || req.query.Bar
});
}
You could also add a default value with another || operator if undefined is not valid four you.
server.js:
var fooBarController = require('controllers/fooBarController');
app.get('/Page/:Foo/:Bar', fooBarController.fooBarQueryOrParams);
app.get('/Page', fooBarController.fooBarQueryOrParams);
Option 2: Same thing, but using res.locals, so now there's no need to pass any object to Page.js, all your views will see res.locals properties just by its names, in this case Foo and Bar (not res.locals.Foo and res.locals.Bar).
controllers/fooBarController.js:
exports.fooBarQueryOrParams = function(req, res) {
res.locals.Foo = req.params.Foo || req.query.Foo;
res.locals.Bar = req.params.Bar || req.query.Bar;
res.render('Page.jade');
}
server.js:
var fooBarController = require('controllers/fooBarController');
app.get('/Page/:Foo/:Bar', fooBarController.fooBarQueryOrParams);
app.get('/Page', fooBarController.fooBarQueryOrParams);
Option 3: Always expose everything thought res.locals:
controllers/fooBarController.js:
exports.fooBarQueryOrParams = function(req, res) {
res.render('Page.jade');
}
server.js:
var fooBarController = require('controllers/fooBarController');
app.use(function(req, res, next) {
for (var key in req.params) res.locals[key] = req.params[key];
for (var key in req.query) res.locals[key] = req.query[key];
next();
});
app.get('/Page/:Foo/:Bar', fooBarController.fooBarQueryOrParams);
app.get('/Page', fooBarController.fooBarQueryOrParams);
I would go for the first option as I suppose you don't really need to use Foo and Bar in all your views, so there's no point in using res.locals to expose them to all your views instead of just to the ones that really need them.
You could just set the object properties yourself and continue to the next route etc
app.use('/Page/:Foo/:Bar',function(req, res, next){
for (var key in req.params) {
req.query[key] = req.params[key];
}
next();
});
I am trying to write a module that creates generic handlers for express routes
e.g.
//create a new route handler with some config
//every routeHanlder method needs to be able to access this config
var handler = new routeHandler({config: "value"});
//handle a get route ("Example 1")
app.get('route', handler.read)
//handle a get route with params ("Example 2")
app.get('route.:id', function(req, res){
handler.read(req,res,{query: {_id: req.params.id}});
});
I am having trouble making "example 1" work...
app.get('route', handler.read)
...as I loose the value of 'this' inside handler.read
I understand why the value of 'this' is different, but I can't figure out how to make it work, or another way to get the desired results without using 'this'.
Here is a plunker link
To summarise I am trying to find a way to make my routeHandler objects (see plunker above, and code paste below) work when used as the callback of an express route (see "example 1" above).
var routeHandler = function(config){
if (!(this instanceof(routeHandler))) {
return new routeHandler(config);
}
config = config || {};
if(config.configData){
this.configData = config.configData;
}
};
routeHandler.prototype = {
read: function(req, res, options){
//The problem: accessing configData without using this
console.log("inside callback", this, this.configData);
options = options || {};
}
};
Edit: I would like the ability to create multiple instances of the route handler with different config data e.g.
var handlerOne = new RouteHandler("configDataOne");
var handlerTwo = new RouteHandler("configDataTwo");
app.get('/firstRoute', handlerOne.read);
app.get('/secondRoute', handlerTwo.read);
You can save routeHandler's configData in express object "app" like below:
app.set("routeHandlerConfigData", "identifier or whatever value you want to store");
then make your routeHandler a simple middleware
var routeHandler = function(req, res, next){
var configData = req.app.get("routeHandlerConfigData");
//Do whatever you want
};
I was inspired by a great comment form yerforkferchips who suggested adding my routerHandler functions inside the constructor like this
this.read = (function read(...) { ... }).bind(this);
Which lets me do exactly what I wanted in my question
app.get('route', handler.read);
BUT i realised that I could use closures in my prototype functions which would sort my 'this' problem and that I would also be able to take in configuration data without having to wrap handler.read in a separate callback function on app.get
RouteHandler.prototype = {
read: function(config){
return function(req, res){
//I have access to req, res and config
}
}
}
so now I can do this
app.get('route', handler.read("configData"));
instead of this
app.get('route', function(req, res){
hander.read(req, res, "configData");
});
What I am trying to do:
Using middleware function I want to store user data from session in to locals so I can use it later in my views without store those data in every method / controller.
Instead of (in every method / controller):
function(req, res){
data.user = req.session.user;
res.render('someView', data);
}
and in View (template):
<div>
#model.user.username
</div>
i want to define middleware like this:
if (req.session.user) {
var now = new Date().getTime();
var data = req.session.user;
var timeout = data.loggedIn + config.auth.timeout;
if (timeout < now) {
req.session.user = null;
reg.flash('errors', {
param: 'default',
msg: 'Your session is expired...'
});
return res.redirect('/login');
}
res.locals.user = sesData;
}
return next();
and in view display my data from res.locals. And there is my BIG questionmark... is it possible ?
I could find any information how to display data from res.locals in vash views.
Am i missing something, is my solution plausible ?
UPDATE
Finally i found solution. Displaying app.locals in VASH views is quite simple :-)
View:
<div>
#model._locals.user.username
</div>
would do what i need :-)
I hope someone will find it useful.
I'm trying to make a single page application with dynamic content, using durandaljs. For example, if you change the language in your settings, then the UI gets updated. I'm using SignalR to load the objects from the server, and everything works fine apart from when I navigate. The first time I load the view, I'm getting the following error:
Uncaught Error: Unable to parse bindings.
Message: ReferenceError: router is not defined;
Bindings value: compose: {
model: router.activeItem, //wiring the router
afterCompose: router.afterCompose, //wiring the router
transition:'entrance', //use the 'entrance' transition when switching views
cacheViews:true //telling composition to keep views in the dom, and reuse them (only a good idea with singleton view models)
}
but if I reload the page, then the view is displayed correctly.
Here is an example of the viewmodel:
define(function (require) {
var p = require('hubs/myhub'),
rep = require('repositories/myrepository');
var myViewModel = function(data, proxy, cookie) {
var self = this;
self.proxy = proxy;
self.cookie = cookie;
self.Labels = ko.observableArray([]);
try {
self.proxy
.invoke('Setup', self.cookie.Username, self.cookie.Language)
.done(function (res) {
if (res.Result) {
console.log(JSON.stringify(res.Object, null, 4));
self.Labels(res.Object.Labels);
} else {
console.log(res.Error);
}
});
} catch (e) {
console.log(e.message);
}
};
return {
activate: function () {
var cookie = JSON.parse($.cookie(rep.cookieName));
ko.applyBindings(myViewModel({}, p.proxy, cookie), document.getElementById('my_container'));
}
};
});
If I take off the applyBinding of the activate function, then there is no more issue within the navigation. Would there be proper way to do this?
I've modified the return statement for:
return {
myModel: new myViewModel ({ }, p.proxy, JSON.parse($.cookie(rep.cookieName))),
activate: function () {
this.myModel.init();
}
};
and wrapped the signalr call inside an init() function. everything works great now.
That is exactly the right way! DUrandal calls the Ko.applybindings for YOU ;) Meaning Durandal does the binding!
Hot Towel SPA Durandal Knockout and Dynatree