Comparing version 4.0.1 to 4.1.0
50
API.md
@@ -10,8 +10,11 @@ # API Reference | ||
- [`server.views(options)`](#serverviewsoptions) | ||
- [`server.render(template, context, [options], callback)`](#serverrendertemplate-context-options-callback) | ||
- [`server.render(template, context, [options], [callback])`](#serverrendertemplate-context-options-callback) | ||
- [Requests](#requests) | ||
- [`request.render(template, context, [options], callback)`](#requestrendertemplate-context-options-callback) | ||
- [`request.render(template, context, [options], [callback])`](#requestrendertemplate-context-options-callback) | ||
- [The `view` handler](#the-view-handler) | ||
- [Reply interface](#reply-interface) | ||
- [`reply.view(template, [context, [options]])`](#replyviewtemplate-context-options) | ||
- [View Manager](#view-manager) | ||
- [`manager.registerHelper(name, helper)`](#managerregisterhelpername-helper) | ||
- [`manager.render(template, context, options, callback)`](#managerrendertemplate-context-options-callback) | ||
@@ -30,8 +33,8 @@ ## [Server](https://github.com/hapijs/hapi/blob/master/API.md#server) | ||
- `compile()` - the rendering function. The required function signature depends on the | ||
`compileMode` settings. If the `compileMode` is `'sync'`, the signature is | ||
`compileMode` settings (see below). If `compileMode` is `'sync'`, the signature is | ||
`compile(template, options)`, the return value is a function with signature | ||
`function(context, options)`, and the method is allowed to throw errors. If the | ||
`compileMode` is `'async'`, the signature is `compile(template, options, callback)` | ||
where `callback` has the signature `function(err, compiled)` where `compiled` is a | ||
function with signature `function(context, options, callback)` and `callback` has the | ||
`function(context, options)` (the compiled sync template), and the method is allowed to throw errors. If | ||
`compileMode` is `'async'`, the signature is `compile(template, options, next)` | ||
where `next` has the signature `function(err, compiled)`, `compiled` is a | ||
function with signature `function(context, options, callback)` (the compiled async template) and `callback` has the | ||
signature `function(err, rendered)`. | ||
@@ -95,6 +98,12 @@ - `prepare(config, next)` - initializes additional engine state. | ||
- `context` - a global context used with all templates. The global context option can be either | ||
an object or a function that takes no arguments and returns a context object. When rendering | ||
views, the global context will be merged with any context object specified on the handler or | ||
using [`reply.view()`](https://github.com/hapijs/hapi/blob/master/API.md#replyviewtemplate-context-options). | ||
When multiple context objects are used, values from the global context always have lowest precedence. | ||
an object or a function that takes the [`request`](https://github.com/hapijs/hapi/blob/master/API.md#request-properties) | ||
as its only argument and returns a context object. The | ||
[`request`](https://github.com/hapijs/hapi/blob/master/API.md#request-properties) object is only provided when using | ||
the [view handler](#the-view-handler) or [`reply.view()`](#replyviewtemplate-context-options). When using | ||
[`server.render()`](#serverrendertemplate-context-options-callback) or | ||
[`request.render()`](#requestrendertemplate-context-options-callback), the | ||
[`request`](https://github.com/hapijs/hapi/blob/master/API.md#request-properties) argument will be `null`. When rendering | ||
views, the global context will be merged with any context object specified on the handler or using | ||
[`reply.view()`](#replyviewtemplate-context-options). When multiple context objects are used, values from the global | ||
context always have lowest precedence. | ||
@@ -105,4 +114,6 @@ When [`server.views()`](https://github.com/hapijs/hapi/blob/master/API.md#serverviewsoptions) is called within a | ||
### `server.render(template, context, [options], callback)` | ||
`server.views()` returns a [view manager](#view-manager) that can be used to programmatically manipulate the engine configuration. | ||
### `server.render(template, context, [options], [callback])` | ||
Utilizes the server views manager to render a template where: | ||
@@ -119,2 +130,5 @@ - `template` - the template filename and path, relative to the views manager templates path (`path` | ||
If no `callback` is provided, a `Promise` object is returned. The returned promise is resolved with only the | ||
rendered content an not the configuration object. | ||
```js | ||
@@ -150,3 +164,3 @@ const Hapi = require('hapi'); | ||
### `request.render(template, context, [options], callback)` | ||
### `request.render(template, context, [options], [callback])` | ||
@@ -310,1 +324,11 @@ `request.render()` works the same way as [`server.render()`](#serverrendertemplate-context-options-callback) | ||
``` | ||
## View Manager | ||
## `manager.registerHelper(name, helper)` | ||
Registers a helper, on all configured engines that have a `registerHelper()` method, for use during template rendering. Engines without a `registerHelper()` method will be skipped. The `name` is the name that templates should use to reference the helper and `helper` is the function that will be invoked when the helper is called. | ||
## `manager.render(template, context, options, [callback])` | ||
Renders a template. This is typically not needed and it is usually more convenient to use [`server.render()`](#serverrendertemplate-context-options-callback). |
@@ -29,2 +29,6 @@ 'use strict'; | ||
if (err) { | ||
throw error; | ||
} | ||
const partials = {}; | ||
@@ -31,0 +35,0 @@ |
@@ -40,3 +40,5 @@ 'use strict'; | ||
this.realm.plugins.vision.manager = new Manager(options); | ||
const manager = new Manager(options); | ||
this.realm.plugins.vision.manager = manager; | ||
return manager; | ||
}); | ||
@@ -68,4 +70,6 @@ | ||
callback = (typeof callback === 'function' ? callback : options); | ||
options = (options === callback ? {} : options); | ||
if (!callback && typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
@@ -105,3 +109,3 @@ const isServer = (typeof this.route === 'function'); | ||
const keys = Object.keys(settings.context); | ||
for (let i = 0, il = keys.length; i < il; ++i) { | ||
for (let i = 0; i < keys.length; ++i) { | ||
const key = keys[i]; | ||
@@ -108,0 +112,0 @@ context[key] = settings.context[key]; |
@@ -260,3 +260,5 @@ 'use strict'; | ||
} | ||
catch (err) { } | ||
catch (err) { | ||
console.warn('WARNING: vision failed to load helper \'%s\': %s', file, err.message); | ||
} | ||
} | ||
@@ -268,4 +270,21 @@ }); | ||
internals.Manager.prototype.registerHelper = function (name, helper) { | ||
Object.keys(this._engines).forEach((extension) => { | ||
const engine = this._engines[extension]; | ||
if (typeof engine.module.registerHelper === 'function') { | ||
engine.module.registerHelper(name, helper); | ||
} | ||
}); | ||
}; | ||
internals.Manager.prototype.render = function (filename, context, options, callback) { | ||
if (!callback) { | ||
return internals._wrapMethod(this, this.render, [filename, context, options]); | ||
} | ||
this._prepare(filename, options, (err, compiled) => { | ||
@@ -277,3 +296,3 @@ | ||
this._render(compiled, context, (err, rendered) => { | ||
this._render(compiled, context, null, (err, rendered) => { | ||
@@ -424,3 +443,3 @@ if (err) { | ||
for (let i = 0, il = paths.length; i < il; ++i) { | ||
for (let i = 0; i < paths.length; ++i) { | ||
paths[i] = internals.path(settings.relativeTo, paths[i], template); | ||
@@ -450,10 +469,10 @@ } | ||
internals.Manager.prototype._render = function (compiled, context, callback) { | ||
internals.Manager.prototype._render = function (compiled, context, request, callback) { | ||
if (this._context) { | ||
let base = typeof this._context === 'function' ? this._context() : this._context; | ||
let base = typeof this._context === 'function' ? this._context(request) : this._context; | ||
if (context) { | ||
base = Hoek.shallow(base); | ||
const keys = Object.keys(context); | ||
for (let i = 0, il = keys.length; i < il; ++i) { | ||
for (let i = 0; i < keys.length; ++i) { | ||
const key = keys[i]; | ||
@@ -567,3 +586,3 @@ base[key] = context[key]; | ||
manager._render(response.source.compiled, response.source.context, (err, rendered) => { | ||
manager._render(response.source.compiled, response.source.context, response.request, (err, rendered) => { | ||
@@ -600,1 +619,18 @@ if (err) { | ||
}; | ||
internals._wrapMethod = (bind, method, args) => { | ||
return new Promise((resolve, reject) => { | ||
const callback = function (err, result) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
return resolve(result); | ||
}; | ||
method.apply(bind, args.concat(callback)); | ||
}); | ||
}; |
{ | ||
"name": "vision", | ||
"description": "Templates rendering plugin support for hapi.js", | ||
"version": "4.0.1", | ||
"version": "4.1.0", | ||
"repository": "git://github.com/hapijs/vision", | ||
@@ -20,3 +20,3 @@ "main": "lib/index.js", | ||
"items": "2.x.x", | ||
"joi": "7.x.x" | ||
"joi": "8.x.x" | ||
}, | ||
@@ -29,7 +29,7 @@ "devDependencies": { | ||
"handlebars": "4.x.x", | ||
"hapi": "11.x.x", | ||
"hapi-react-views": "5.x.x", | ||
"hapi": "13.x.x", | ||
"hapi-react-views": "6.x.x", | ||
"jade": "1.x.x", | ||
"lab": "7.x.x", | ||
"marko": "2.x.x", | ||
"lab": "10.x.x", | ||
"marko": "3.x.x", | ||
"mustache": "2.x.x", | ||
@@ -36,0 +36,0 @@ "nunjucks": "2.x.0", |
@@ -160,3 +160,3 @@ 'use strict'; | ||
it('handles a global context', (done) => { | ||
it('handles a global context object', (done) => { | ||
@@ -183,2 +183,28 @@ const server = new Hapi.Server(); | ||
it('passes the request to a global context function', (done) => { | ||
const server = new Hapi.Server(); | ||
server.connection(); | ||
server.register(Vision, Hoek.ignore); | ||
server.views({ | ||
engines: { html: require('handlebars') }, | ||
path: __dirname + '/templates', | ||
context: (request) => { | ||
return { | ||
message: request ? request.route.path : 'default' | ||
}; | ||
} | ||
}); | ||
server.route({ method: 'GET', path: '/', handler: { view: { template: 'valid/testContext' } } }); | ||
server.inject('/', (res) => { | ||
expect(res.result).to.contain('<h1></h1>'); | ||
expect(res.result).to.contain('<h1>/</h1>'); | ||
done(); | ||
}); | ||
}); | ||
it('overrides the global context with the default handler context', (done) => { | ||
@@ -274,2 +300,3 @@ | ||
expect(err).not.to.exist(); | ||
expect(rendered).to.exist(); | ||
@@ -293,2 +320,3 @@ expect(rendered).to.contain('Hapi'); | ||
expect(err).not.to.exist(); | ||
expect(rendered).to.exist(); | ||
@@ -311,2 +339,4 @@ expect(rendered).to.contain('Hapi'); | ||
expect(err).not.to.exist(); | ||
server.route([ | ||
@@ -350,2 +380,4 @@ { | ||
expect(err).not.to.exist(); | ||
server.route([ | ||
@@ -398,2 +430,4 @@ { | ||
expect(err).not.to.exist(); | ||
server.route([ | ||
@@ -458,2 +492,3 @@ { | ||
expect(err).not.to.exist(); | ||
return reply(rendered); | ||
@@ -485,2 +520,142 @@ }); | ||
}); | ||
it('does not pass the request to the global context function (server)', (done) => { | ||
const server = new Hapi.Server(); | ||
server.connection(); | ||
server.register(Vision, Hoek.ignore); | ||
server.views({ | ||
engines: { html: require('handlebars') }, | ||
path: __dirname + '/templates/valid', | ||
context: (request) => { | ||
return { | ||
message: request ? request.route.path : 'default' | ||
}; | ||
} | ||
}); | ||
server.render('testContext', null, null, (err, result) => { | ||
expect(err).not.to.exist(); | ||
expect(result).to.contain('<h1>default</h1>'); | ||
done(); | ||
}); | ||
}); | ||
it('does not pass the request to the global context function (request)', (done) => { | ||
const server = new Hapi.Server(); | ||
server.connection(); | ||
server.register(Vision, Hoek.ignore); | ||
server.views({ | ||
engines: { html: require('handlebars') }, | ||
path: __dirname + '/templates/valid', | ||
context: (request) => { | ||
return { | ||
message: request ? request.route.path : 'default' | ||
}; | ||
} | ||
}); | ||
const handler = (request, reply) => { | ||
request.render('testContext', null, null, (err, rendered) => { | ||
expect(err).not.to.exist(); | ||
reply(rendered); | ||
}); | ||
}; | ||
server.route({ method: 'GET', path: '/', handler: handler }); | ||
server.inject({ method: 'GET', url: '/' }, (response) => { | ||
expect(response.result).to.contain('<h1>default</h1>'); | ||
done(); | ||
}); | ||
}); | ||
it('returns a promise when no options or callback given (server)', () => { | ||
const server = new Hapi.Server(); | ||
server.connection(); | ||
server.register(Vision, Hoek.ignore); | ||
server.views({ | ||
engines: { html: Handlebars }, | ||
path: __dirname + '/templates/valid' | ||
}); | ||
return server.render('test', { message: 'Hello!' }) | ||
.then((content) => expect(content).to.contain('<h1>Hello!</h1>')); | ||
}); | ||
it('returns a promise when no callback given (server)', () => { | ||
const server = new Hapi.Server(); | ||
server.connection(); | ||
server.register(Vision, Hoek.ignore); | ||
server.views({ | ||
engines: { html: Handlebars }, | ||
path: __dirname + '/templates/valid' | ||
}); | ||
return server.render('test', { message: 'Hello!' }, {}) | ||
.then((content) => expect(content).to.contain('<h1>Hello!</h1>')); | ||
}); | ||
it('returns a promise when no options or callback given (request)', (done) => { | ||
const server = new Hapi.Server(); | ||
server.connection(); | ||
server.register(Vision, Hoek.ignore); | ||
server.views({ | ||
engines: { html: Handlebars }, | ||
path: __dirname + '/templates/valid' | ||
}); | ||
const handler = (request, reply) => { | ||
const promise = request.render('test', { message: 'Hello!' }); | ||
expect(promise).to.be.an.instanceof(Promise); | ||
reply(promise); | ||
}; | ||
server.route({ method: 'GET', path: '/', handler: handler }); | ||
server.inject({ method: 'GET', url: '/' }, (response) => { | ||
expect(response.result).to.contain('<h1>Hello!</h1>'); | ||
done(); | ||
}); | ||
}); | ||
it('returns a promise when no callback given (request)', (done) => { | ||
const server = new Hapi.Server(); | ||
server.connection(); | ||
server.register(Vision, Hoek.ignore); | ||
server.views({ | ||
engines: { html: Handlebars }, | ||
path: __dirname + '/templates/valid' | ||
}); | ||
const handler = (request, reply) => { | ||
const promise = request.render('test', { message: 'Hello!' }, {}); | ||
expect(promise).to.be.an.instanceof(Promise); | ||
reply(promise); | ||
}; | ||
server.route({ method: 'GET', path: '/', handler: handler }); | ||
server.inject({ method: 'GET', url: '/' }, (response) => { | ||
expect(response.result).to.contain('<h1>Hello!</h1>'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -646,2 +821,43 @@ | ||
}); | ||
it('can register helpers via the view manager', (done) => { | ||
const server = new Hapi.Server(); | ||
server.register(Vision, Hoek.ignore); | ||
const manager = server.views({ | ||
engines: { 'html': Handlebars.create() }, | ||
relativeTo: 'test/templates', | ||
path: 'valid' | ||
}); | ||
manager.registerHelper('long', (string) => string); | ||
manager.registerHelper('uppercase', (string) => string); | ||
server.render('testHelpers', { something: 'uppercase' }, (err, result) => { | ||
expect(err).not.to.exist(); | ||
expect(result).to.equal('<p>This is all uppercase and this is how we like it!</p>'); | ||
done(); | ||
}); | ||
}); | ||
it('can render templates via the view manager', (done) => { | ||
const server = new Hapi.Server(); | ||
server.register(Vision, Hoek.ignore); | ||
const manager = server.views({ | ||
engines: { 'html': Handlebars }, | ||
relativeTo: 'test/templates', | ||
path: 'valid' | ||
}); | ||
manager.render('test', { message: 'Hello!' }, null, (err, result) => { | ||
expect(err).not.to.exist(); | ||
expect(result).to.contain('<h1>Hello!</h1>'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -648,0 +864,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
174120
106
3530