Serving client side Jade templates - node.js

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

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');

Node.js - How to grab the class names from a .scss file

I wanted to ask if anyone knows of a good solution for how to use node.js to look in a .scss file and grab all the classes listed and to then put them in either an object or an array?
The thing with this is that you are going to need the sass folder to be available to you server, this is not a recommended practice since you only publish the css compiled file, there is no need to also publish the dev assets.
However if you do so, you will need to read .scss file using node and from there use a regex to match the .class strings inside the file.
This will make the reading of the file:
var fs = require('fs');
function readSassFile () {
fs.readFile('./public/scss/components/_styles.scss', 'utf8', function (err, data) {
if (err) {
console.log(err);
return;
}
regexArray(data);
});
}
As you can see, at the end if the readFile retrieves the file with success, I'm calling a function regexArray() and sending the data of the file loaded.
In the regexArray function you need to define a regex to evaluate the string of the file loaded.
function regexArray (data) {
var re = /\.\S*/g;
var m;
var classArray = [];
while ((m = re.exec(data)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
classArray.push(m[0]);
}
console.log(classArray);
}
the var re is the regular expression matching any string starting with a . and ending with a non-whitespace character which will match your css class names.
then we evaluate the m variable when is different from null and store the results in the array classArray, then you can log it to see the results.
I made the test with the path that is in the fs.readFile method, you can change it for you own path.

Meteor/Node writeFile crashes server

I have the following code:
Meteor.methods({
saveFile: function(blob, name, path, encoding) {
var path = cleanPath(path), fs = __meteor_bootstrap__.require('fs'),
name = cleanName(name || 'file'), encoding = encoding || 'binary',
chroot = Meteor.chroot || 'public';
// Clean up the path. Remove any initial and final '/' -we prefix them-,
// any sort of attempt to go to the parent directory '..' and any empty directories in
// between '/////' - which may happen after removing '..'
path = chroot + (path ? '/' + path + '/' : '/');
// TODO Add file existance checks, etc...
fs.writeFile(path + name, blob, encoding, function(err) {
if (err) {
throw (new Meteor.Error(500, 'Failed to save file.', err));
} else {
console.log('The file ' + name + ' (' + encoding + ') was saved to ' + path);
}
});
function cleanPath(str) {
if (str) {
return str.replace(/\.\./g,'').replace(/\/+/g,'').
replace(/^\/+/,'').replace(/\/+$/,'');
}
}
function cleanName(str) {
return str.replace(/\.\./g,'').replace(/\//g,'');
}
}
});
Which I took from this project
https://gist.github.com/dariocravero/3922137
The code works fine, and it saves the file, however it repeats the call several time and each time it causes meteor to reset using windows version 0.5.4. The F12 console ends up looking like this: . The meteor console loops over the startup code each time the 503 happens and repeats the console logs in the saveFile function.
Furthermore in the target directory the image thumbnail keeps displaying and then display as broken, then a valid thumbnail again, as if the fs is writing it multiple times.
Here is the code that calls the function:
"click .savePhoto":function(e, template){
e.preventDefault();
var MAX_WIDTH = 400;
var MAX_HEIGHT = 300;
var id = e.srcElement.id;
var item = Session.get("employeeItem");
var file = template.find('input[name='+id+']').files[0];
// $(template).append("Loading...");
var dataURL = '/.bgimages/'+file.name;
Meteor.saveFile(file, file.name, "/.bgimages/", function(){
if(id=="goodPhoto"){
EmployeeCollection.update(item._id, { $set: { good_photo: dataURL }});
}else{
EmployeeCollection.update(item._id, { $set: { bad_photo: dataURL }});
}
// Update an image on the page with the data
$(template.find('img.'+id)).delay(1000).attr('src', dataURL);
});
},
What's causing the server to reset?
My guess would be that since Meteor has a built-in "automatic directories scanning in search for file changes", in order to implement auto relaunching of the application to newest code-base, the file you are creating is actually causing the server reset.
Meteor doesn't scan directories beginning with a dot (so called "hidden" directories) such as .git for example, so you could use this behaviour to your advantage by setting the path of your files to a .directory of your own.
You should also consider using writeFileSync insofar as Meteor methods are intended to run synchronously (inside node fibers) contrary to the usual node way of asynchronous calls, in this code it's no big deal but for example you couldn't use any Meteor mechanics inside the writeFile callback.
asynchronousCall(function(error,result){
if(error){
// handle error
}
else{
// do something with result
Collection.update(id,result);// error ! Meteor code must run inside fiber
}
});
var result=synchronousCall();
Collection.update(id,result);// good to go !
Of course there is a way to turn any asynchronous call inside a synchronous one using fibers/future, but that's beyond the point of this question : I recommend reading this EventedMind episode on node future to understand this specific area.

Nodejs Backbone Templates

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'

Jade.escape is undefined on compiled Jade template

I've compiled a jade template like:
jade --client --no-debug ...
Then on client side included jade.js and the compiled template file. But jade.escape is undefined. I notice the compiled template function looks like:
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("");
}
Notice how escape is passed in as a parameter. So how might the expected usage be like?
Suppose I have a very simple template:
h1= title
Then I use it like:
html = anonymous({title: "Hello World!"})
But it means escape will be undefined? I notice jade.escape is also undefined although I included jade.js
you need to add runtime.js along with jade.js inorder to work with jade.escape.

Resources