Nodejs Backbone Templates - node.js

I have worked a lot with rails, requirejs and backbone and know how to use haml coffee templates in rails.
App = new Backbone.Marionette.Application()
App.addInitializer (options) ->
Backbone.history.start()
alert "yay"
$ ->
alert "yay"
App.start()
How do i do it in Node.js, I have a Node.js app and i am at a deadend with regards to how do i get a template to compile client side, i am not stuck on haml coffee, any template engine will do, jade is fine too, underscore too. Just a good starting point so that i can get on with building the backbone app in node.js.
Any Help is appreciated!

I don't suggest dragging the templates to the client and compiling them there,the right way would be to use some framework such as www.socketstream.com that offers what you want and much more. If you're against frameworks quick and dirty solution to compiling them on the server and calling them as function on the client will be :
// compile.js
var fs = require("fs")
,jade = require("jade");
exports.build = function(templatesDir) {
var js = "var Templates = {}; \n\n";
var files = fs.readdirSync(templatesDir);
var jadeFiles = files.filter(function(file) {
return file.substr(-5) === ".jade";
});
for(var i = 0; i < jadeFiles.length; ++i){
var filePath, key;
var file = jadeFiles[i];
key = file.substr(0, file.indexOf("."));
filePath = templatesDir + file;
var jadeSource = fs.readFileSync(filePath);
js += "Templates." + key + " = " + jade.compile(jadeSource, {
debug: false,
client: true
}).toString() + "; \n\n";
}
return js;
};
// On the server.js
// Compile views
var viewsPath = path.join(__dirname, 'views/');
var generatedJs = require('./compile').build(viewsPath);
var savePath = path.join(__dirname, 'public/js/lib/templates.js');
fs.writeFile(savePath, generatedJs, function (err) {
if (err) throw err;
});
// Then on the client include js/lib/templates.js and use templates like this
FactSummaryView = Backbone.View.extend({
template: Templates.issueSummary,
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
// Also add templates.js to nodemonignore if you're using nodemon
./public/js/lib/templates.js
/public/js/lib/templates.js

You usually don't compile the templates on the client (expect the templates are edited directly in the browser), instead they are compiled in the backend and rendered in the browser.
Compile the templates
In this step you compile the template source code to a JavaScript file that contains a render function.
You can either use haml-coffee on the command line and make a script in your build process or make use of the projects listed in the integration section of the Haml-Coffee README.
Grunt is a popular solution to run certain tasks and with Grunt-Haml you have certainly a flexible build solution for your project.
Render the templates
To render the templates with Marionette you need to make sure the template render function is available on the client. Just type the configured namespace into the developer tools to see if the template functions are registered:
If this is fine, you need to have a custom template render function:
Backbone.Marionette.Renderer.render = (template, data) ->
if JST[template]
JST[template](data)
else if _.isFunction(template)
template(data)
else
console.error 'Template %s not found', template
Now you can simply define the view template and it'll be rendered properly:
class App.Views.Login extends Backbone.Marionette.ItemView
template: 'shared/_login'

Related

change requireJS from adding the .js file extension automatically on intern.js

Currently I am working in custom html reporter for intern.js. The Templating engine that i am using is marko.js.
marko.js have extension file with ".marko" for me to input my html syntax
The file is generated correctly in normal node.js (common.js)
The issue occurred when i integrate the same code to intern.js. The requirejs(AMD) that use by internjs is adding the .js file extension automatically to my marko extension when i do
var template = require('./hello-world.marko');
which make the file become hello-world.marko.js and this caused the code broke in markojs
the custom html reporter code is below
define(function (require) {
// require('intern/dojo/node!marko/node-require').install();
var fs = require('intern/dojo/node!fs');
var template = require('./hello-world.marko');
console.log(template);
function JsonReporter(config) {
config = config || {};
this.output = config.output;
}
JsonReporter.prototype = {
runEnd(executor) {
// console.log("toJson: " + JSON.stringify(executor.suites))
data = JSON.stringify(executor.suites);
template.renderToString(data,
function (err, output) {
console.log(output);
fs.writeFile('result.html', output, function (err) {
if (err) return console.log(err);
console.log('Save done');
});
});
},
}
return JsonReporter;
})
The require function isn't really meant for loading arbitrary text resources in either Node's loader or an AMD loader. In Node, whether you're running Intern or not, you can use fs.readFile or fs.readFileSync. In Intern's Dojo-based AMD environment you can also use the dojo/text loader plugin, like this:
var template = require('dojo/text!./hello-world.marko');

Gulp, livereload, jade

Need help.
I use gulp-conect and it livereload method. But if I build a few template in time, get a lot of page refresh. Is any solution, I want to build few templates with single page refresh?
So, I reproduce the problem you have and came accross this working solution.
First, lets check gulp plugins you need:
gulp-jade
gulp-livereload
optional: gulp-load-plugins
In case you need some of them go to:
http://gulpjs.com/plugins/
Search for them and install them.
Strategy: I created a gulp task called live that will check your *.jade files, and as you are working on a certain file & saving it, gulp will compile it into html and refresh the browser.
In order to accomplish that, we define a function called compileAndRefresh that will take the file returned by the watcher. It will compile that file into html and the refesh the browser (test with livereload plugin for chrome).
Notes:
I always use gulp-load-plugin to load plugins, so thats whay I use plugins.jad and plugins.livereload.
This will only compile files that are saved and while you have the task live exucting on the command line. Will not compile other files that are not in use. In order to accomplish that, you need to define a task that compiles all files, not only the ones that have been changed.
Assume .jade files in /jade and html output to /html
So, here is the gulpfile.js:
var gulp = require('gulp'),
gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins();
gulp.task('webserver', function() {
gulp.src('./html')
.pipe(plugins.webserver({
livereload: true
}));
gulp.watch('./jade/*.jade', function(event) {
compileAndRefresh(event.path);
});
});
function compileAndRefresh(file) {
gulp.src(file)
.pipe(plugins.jade({
}))
.pipe(gulp.dest('./html'))
}
Post edit notes:
Removed liveReload call from compileAndRefresh (webserver will do that).
Use gulp-server plugin insted of gulp-connect, as they suggest on their repository: "New plugin based on connect 3 using the gulp.src() API. Written in plain javascript. https://github.com/schickling/gulp-webserver"
Something you can do is to watch only files that changes, and then apply a function only to those files that have been changed, something like this:
gulp.task('live', function() {
gulp.watch('templates/folder', function(event) {
refresh_templates(event.path);
});
});
function refresh_templates(file) {
return
gulp.src(file)
.pipe(plugins.embedlr())
.pipe(plugins.livereload());
}
PS: this is not a working example, and I dont know if you are using embedlr, but the point, is that you can watch, and use a callback to call another function with the files that are changing, and the manipulate only those files. Also, I supposed that your goal is to refresh the templates for your browser, but you manipulate as you like, save them on dest or do whatever you want.
Key point here is to show how to manipulate file that changes: callback of watch + custom function.
var jadeTask = function(path) {
path = path || loc.jade + '/*.jade';
if (/source/.test(path)) {
path = loc.jade + '/**/*.jade';
}
return gulp.src(path)
.pipe(changed(loc.markup, {extension: '.html'}))
.pipe(jade({
locals : json_array,
pretty : true
}))
.pipe(gulp.dest(loc.markup))
.pipe(connect.reload());
}
First install required plugins
gulp
express
gulp-jade
connect-livereload
tiny-lr
connect
then write the code
var gulp = require('gulp');
var express = require('express');
var path = require('path');
var connect = require("connect");
var jade = require('gulp-jade');
var app = express();
gulp.task('express', function() {
app.use(require('connect-livereload')({port: 8002}));
app.use(express.static(path.join(__dirname, '/dist')));
app.listen(8000);
});
var tinylr;
gulp.task('livereload', function() {
tinylr = require('tiny-lr')();
tinylr.listen(8002);
});
function notifyLiveReload(event) {
var fileName = require('path').relative(__dirname, event.path);
tinylr.changed({
body: {
files: [fileName]
}
});
}
gulp.task('jade', function(){
gulp.src('src/*.jade')
.pipe(jade())
.pipe(gulp.dest('dist'))
});
gulp.task('watch', function() {
gulp.watch('dist/*.html', notifyLiveReload);
gulp.watch('src/*.jade', ['jade']);
});
gulp.task('default', ['livereload', 'express', 'watch', 'jade'], function() {
});
find the example here at GitHub

Serving client side Jade templates

I want to use Jade templates at the client side with Backbone. How can I do that?
For now, I have successfully configured Backbone (Marionette) to compile Jade templates for use in its Views:
Marionette.TemplateCache.prototype.compileTemplate = (tmplStr) ->
console.log "jade stuff: ", jade.compile(tmplStr)
return jade.compile(tmplStr)
The "problem" is: I am currently writing templates like:
script(type="text/template", id="tmplMainView")
| h1= title
| p= content
Notice the pipes (|) those are to prevent Jade from trying to interpret/parse them on server side. How can I eliminate those?
UPDATE
Perhaps I can use the jade --client flag ... but it gives a single compiled function: for example
h1= title
Becomes
function anonymous(locals, attrs, escape, rethrow, merge) {
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;
var buf = [];
with (locals || {}) {
var interp;
buf.push('<h1>');
var __val__ = title
buf.push(escape(null == __val__ ? "" : __val__));
buf.push('</h1>');
}
return buf.join("");
}
That means I have to have 1 Jade/compiled JS for each template? How might I use it? Also I think many JS files is a slow way to work? But since template functions are all named anonymous, how can I then concat or somehow work with them effectively?
Check the ClientJade project.
From their site:
clientjade is a command line tool to compile your jade templates into client side templates for use in the browser. It will automatically include everything you need to render the templates, no need to include jade.js or runtime.js.
$ clientjade test1.jade test2.jade > templates.js
And then include template.js file in your html. To render the templates, just make a call like this:
//jade.render(domNode, templateName, data);
jade.render(document.getElementById('test1'), 'test1', { name: 'Bob' });
After looking at Jadify and ClientJade, and encountering a few problems along the way ... (perhaps its just somethings I missed out), I decided to explore simply compiling the templates on server side.
I defined a Node module (used by ExpressJS) which does the compilation and returns the compiled JS source (which I served with /js/templates.js).
fs = require "fs"
jade = require "jade"
async = require "async"
# done callback will be passed (source, err)
exports.compile = (done, templatesDir) ->
js = "var Templates = {}; \n\n"
# get all files in templates directory
fs.readdir templatesDir, (err, files) ->
# keep only ".jade" files
jadeFiles = files.filter (file) ->
file.substr(-5) == ".jade"
# function to compile jade templates (appending to js source)
compileTmpl = (file, doneCompile) ->
# "test.jade" becomes "test"
key = file.substr(0, file.indexOf("."))
filePath = templatesDir + file
fs.readFile filePath, (err, src) ->
# store js function source into Templates.{key}
js += "Templates." + key + " = " + jade.compile(src, { debug: false, client: true }).toString() + "; \n\n"
doneCompile(err)
# foreach jadeFile, compile template, then write templates.js file
async.forEach jadeFiles, compileTmpl, (err) ->
done(js, err)
And I use the precompiled templates on client side by including the templates.js and use templates like:
Templates.tmpl1()
Templates.tmpl2({ something: "Hello world", ... })
More on https://coderwall.com/p/hlojkw

Configuring $.ajax with backbone on node for testing with vows

(Edited to greatly simplify)
On node I have the following server.js file.
var Backbone = require('backbone');
var Tweet = Backbone.Model.extend({});
var Tweets = Backbone.Collection.extend({
model : Tweet,
url: function () {
return 'http://search.twitter.com/search.json?q=backbone'
}
});
var myTweets = new Tweets();
myTweets.fetch();
When I run this, I get an error that says. "Cannot call method 'ajax' of undefined" (1359:14)
basically that is the result of $ being undefined. Why is it undefined? Well there are a number of intermediate steps but when the file is loaded, it is expecting "this" to be "window" in browser or "global" on server. executed on node "this" = {}.
So the question, "How do I set 'this' to global" inside the backbone.js file?
On Backbone >= 1.x, you can simply assign Backbone.$ rather than using Backbone.setDomLibrary.
Solution for Backbone < 0.9.9
The first issue you need to address is how you are running this on Node anyway. Nodejs is a server-side JS environment, but it does not include any logic for controlling a DOM. For that you need to load something like JSDom.
When you have some DOM environment set up, you can load jQuery and your code into it and it should work just like a browser.
To answer your question specifically though, loading jQuery into the global is a bit of an ugly way to do it. You should use Backbone's setDomLibrary function to set $ to what you want.
Try something like this:
if (typeof exports !== 'undefined') {
MyModels = exports;
Backbone.setDomLibrary(require('jquery'));
server = true;
} else {
MyModels = this.MyModels = {};
}
This will fail if you try to do any DOM functions though.

Multiple View paths on Node.js + Express

I'm writing a CMS on Node.js with Express Framework. On my CMS I have several modules for users, pages, etc.
I want that each module will have his files on separate folder, including the view files.
Anyone know how can I achieve that?
I'm using swig as my template engine but I can replace it to something else if it will helps.
Last Update
The multiple view folders feature is supported by the framework since Express 4.10
Just pass an array of locations to the views property, like so.
app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);
Express 2.0
As far as I know express doesn't support multiple view paths or namespaces at the moment (like the static middleware do)
But you can modify the lookup logic yourself so that it works the way you want, for example:
function enableMultipleViewFolders(express) {
// proxy function to the default view lookup
var lookupProxy = express.view.lookup;
express.view.lookup = function (view, options) {
if (options.root instanceof Array) {
// clones the options object
var opts = {};
for (var key in options) opts[key] = options[key];
// loops through the paths and tries to match the view
var matchedView = null,
roots = opts.root;
for (var i=0; i<roots.length; i++) {
opts.root = roots[i];
matchedView = lookupProxy.call(this, view, opts);
if (matchedView.exists) break;
}
return matchedView;
}
return lookupProxy.call(express.view, view, options)
};
}
You will enable the new logic by calling the function above and passing express as a parameter, and then you will be able to specify an array of views to the configuration:
var express = require('express');
enableMultipleViewFolders(express);
app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);
Or, if you prefer, you can patch the framework directly (updating the view.js file inside it)
This should work in Express 2.x, not sure if it will with the new version (3.x)
UPDATE
Unluckily the above solution won't work in Express 3.x since express.view would be undefined
Another possible solution will be to proxy the response.render function and set the views folder config until it gets a match:
var renderProxy = express.response.render;
express.render = function(){
app.set('views', 'path/to/custom/views');
try {
return renderProxy.apply(this, arguments);
}
catch (e) {}
app.set('views', 'path/to/default/views');
return renderProxy.apply(this, arguments);
};
I've not tested it, it feels very hacky to me anyway, unluckily this feature has been pushed back again:
https://github.com/visionmedia/express/pull/1186
UPDATE 2
This feature has been added in Express 4.10, since the following pull request has been merged:
https://github.com/strongloop/express/pull/2320
In addition to #user85461 answer, the require view part did not work for me.
What i did: removed the path stuff and moved it all to a module i could require,
patch.ViewEnableMultiFolders.js (Works with current express):
function ViewEnableMultiFolders(app) {
// Monkey-patch express to accept multiple paths for looking up views.
// this path may change depending on your setup.
var lookup_proxy = app.get('view').prototype.lookup;
app.get('view').prototype.lookup = function(viewName) {
var context, match;
if (this.root instanceof Array) {
for (var i = 0; i < this.root.length; i++) {
context = {root: this.root[i]};
match = lookup_proxy.call(context, viewName);
if (match) {
return match;
}
}
return null;
}
return lookup_proxy.call(this, viewName);
};
}
module.exports.ViewEnableMultiFolders = ViewEnableMultiFolders;
and used:
var Patch = require('patch.ViewEnableMultiFolders.js');
Patch.ViewEnableMultiFolders(app);
app.set('views', ['./htdocs/views', '/htdocs/tpls']);
Here's a solution for Express 3.x. It monkey-patches express 3.x's "View" object to do the same lookup trick as #ShadowCloud's solution above. Unfortunately, the path lookup for the View object is less clean, since 3.x doesn't expose it to express -- so you have to dig into the bowels of node_modules.
function enable_multiple_view_folders() {
// Monkey-patch express to accept multiple paths for looking up views.
// this path may change depending on your setup.
var View = require("./node_modules/express/lib/view"),
lookup_proxy = View.prototype.lookup;
View.prototype.lookup = function(viewName) {
var context, match;
if (this.root instanceof Array) {
for (var i = 0; i < this.root.length; i++) {
context = {root: this.root[i]};
match = lookup_proxy.call(context, viewName);
if (match) {
return match;
}
}
return null;
}
return lookup_proxy.call(this, viewName);
};
}
enable_multiple_view_folders();
You can however, put all the view files inside the 'view' folder, but separate each module's view into it's own folders inside the 'view' folder. So, the structure is something like this :
views
--moduleA
--moduleB
----submoduleB1
----submoduleB2
--moduleC
Set the view files like usual :
app.set('views', './views');
And when render for each module, include the module's name :
res.render('moduleA/index', ...);
or even submodule's name :
res.render('moduleB/submoduleB1/index', ...);
This solution is also works in express before version 4.x,
Install glob npm install glob
If you have a views directory that looks something like:
views
├── 404.ejs
├── home.ejs
├── includes
│ ├── header.ejs
│ └── footer.ejs
├── post
│ ├── create.ejs
│ └── edit.ejs
└── profile.ejs
You can use this glob function to return an array of subdirectories in the views directory (add the path.substring to remove the trailing /)
let viewPaths = glob.sync('views/**/').map(path => {
return path.substring(0, path.length - 1)
})
console.log(viewPaths)
>> ['views', 'views/post', 'views/includes']
So now you can set
app.set('views', viewPaths)
and now you can use
res.render('404')
res.render('home')
res.render('post/edit')
res.render('post/create')

Resources