Fixes #286 - Automatically pass res.locals to template.render()

This commit is contained in:
Michael Rawlings 2016-05-05 15:19:58 -07:00
parent 977bd09b8c
commit 038c71dc1a
12 changed files with 203 additions and 6 deletions

View File

@ -10,6 +10,10 @@ npm install marko --save
# Usage
Marko provides a submodule (`marko/express`) to add a `res.marko` method to the express response object. This function works much like `res.render`, but doesn't impose the restrictions of the express view engine and allows you to take full advantage of Marko's streaming and modular approach to templates.
By using `res.marko` you'll automatically have access to `req`, `res`, `app`, `app.locals`, and `res.locals` from within your Marko template and custom tags. These values are added to `out.global`.
```javascript
require('marko/node-require').install();
@ -18,13 +22,16 @@ var template = require('./template.marko');
var app = express();
//enable res.marko
require('marko/express').injectInto(express);
app.get('/', function(req, res) {
template.render({
name: 'Frank',
count: 30,
colors: ['red', 'green', 'blue']
}, res);
res.marko(template, {
name: 'Frank',
count: 30,
colors: ['red', 'green', 'blue']
});
});
app.listen(8080);
```
```

32
express.js Normal file
View File

@ -0,0 +1,32 @@
var assign = require('object-assign');
exports.injectInto = function injectInto(express) {
if(express.response.marko) return;
express.response.marko = function(template, data) {
if(typeof template === 'string') {
throw new Error(
'res.marko does not take a template name or path like res.render. ' +
'Instead you should use `require(\'./path/to/template.marko\')` ' +
'and pass the loaded template to this function.'
)
}
var res = this;
var req = res.req;
var app = res.app;
var $global = assign({ app, req, res }, app.locals, res.locals);
if (data) {
data = assign(data, {
$global: assign($global, data.$global)
});
} else {
data = { $global };
}
res.set({ 'content-type': 'text/html; charset=utf-8' });
template.render(data, res);
};
};

View File

@ -16,6 +16,7 @@
"test-fast": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-test",
"test-async": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-async-test",
"test-taglib-loader": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/taglib-loader-test",
"test-express": "node_modules/.bin/mocha --ui bdd --reporter spec ./test/express-test",
"jshint": "node_modules/.bin/jshint compiler/ runtime/ taglibs/"
},
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
@ -33,6 +34,7 @@
"htmljs-parser": "^1.5.3",
"lasso-package-root": "^1.0.0",
"minimatch": "^0.2.14",
"object-assign": "^4.1.0",
"property-handlers": "^1.0.0",
"raptor-args": "^1.0.0",
"raptor-async": "^1.1.2",
@ -50,8 +52,10 @@
"devDependencies": {
"bluebird": "^2.9.30",
"chai": "^3.3.0",
"express": "^4.13.4",
"jshint": "^2.5.0",
"mocha": "^2.3.3",
"request": "^2.72.0",
"through": "^2.3.4"
},
"license": "Apache-2.0",

View File

@ -0,0 +1 @@
<div></div>

View File

@ -0,0 +1,25 @@
exports.createApp = function(express, markoExpress) {
var app = express();
markoExpress.injectInto(express);
app.locals.foo = 'FOO';
app.use(function(req, res, next) {
res.locals.bar = 'BAR';
next();
});
return app;
};
exports.createController = function(template) {
return function(req, res) {
res.marko(template);
};
}
exports.checkResponse = function(response, expect, helpers) {
expect(response.headers['content-type']).to.equal('text/html; charset=utf-8');
expect(response.body).to.equal('<div></div>');
}

View File

@ -0,0 +1 @@
<div>FOOBAR</div>

View File

@ -0,0 +1 @@
<div>${out.global.foo}${out.global.bar}</div>

View File

@ -0,0 +1,20 @@
exports.createApp = function(express, markoExpress) {
markoExpress.injectInto(express);
var app = express();
app.locals.foo = 'FOO';
app.use(function(req, res, next) {
res.locals.bar = 'BAR';
next();
});
return app;
};
exports.createController = function(template) {
return function(req, res) {
res.marko(template);
};
}

View File

@ -0,0 +1 @@
<div>DATARESAPPHELLO</div>

View File

@ -0,0 +1 @@
<div>${out.global.foo}${out.global.bar}${out.global.baz}${data.test}</div>

View File

@ -0,0 +1,23 @@
exports.createApp = function(express, markoExpress) {
markoExpress.injectInto(express);
var app = express();
app.locals.foo = 'APP';
app.locals.bar = 'APP';
app.locals.baz = 'APP';
app.use(function(req, res, next) {
res.locals.foo = 'RES';
res.locals.bar = 'RES';
next();
});
return app;
};
exports.createController = function(template) {
return function(req, res) {
res.marko(template, { $global:{ foo:'DATA' }, test:'HELLO' });
};
}

81
test/express-test.js Normal file
View File

@ -0,0 +1,81 @@
'use strict';
require('./patch-module');
var chai = require('chai');
chai.config.includeStack = true;
var path = require('path');
var marko = require('../');
var autotest = require('./autotest');
var express = require('express');
var markoExpress = require('../express');
var request = require('request');
var fs = require('fs');
require('../node-require').install();
describe('render', function() {
var autoTestDir = path.join(__dirname, 'autotests/express');
autotest.scanDir(
autoTestDir,
function run(dir, helpers, done) {
var mainPath = path.join(dir, 'test.js');
var templatePath = path.join(dir, 'template.marko');
var main = fs.existsSync(mainPath) ? require(mainPath) : {};
var loadOptions = main && main.loadOptions;
if (main.checkError) {
var e;
try {
main.createApp(express, markoExpress);
} catch(_e) {
e = _e;
}
if (!e) {
throw new Error('Error expected');
}
main.checkError(e);
return done();
} else {
var app = main.createApp(express, markoExpress);
var template = marko.load(templatePath, loadOptions);
app.get('/test', main.createController(template));
var server = app.listen(0, function(err) {
if(err) {
return done(err);
}
var port = server.address().port;
var address = `http://localhost:${port}/test`;
request(address, function(error, response, body) {
try {
if(main.checkResponse) {
response.body = body;
response.error = error;
main.checkResponse(response, chai.expect, helpers);
} else {
if(error) {
return done(error);
}
chai.expect(response.statusCode).to.equal(200);
helpers.compare(body, '.html');
}
} catch(error) {
server.close();
throw error;
}
server.close();
done();
});
});
}
});
});