Commit d48e0cbf authored by S Anand's avatar S Anand
Browse files

ENH: $.template() supports src= for AJAX load. Fixes #1

parent d84d70eb
......@@ -49,7 +49,7 @@ page URL instead of replacing it.
| `?x=1` | `?y=1` | `?x=1&y=1` | `?x=1&y=1` | `?x=1&y=1` | `?x=1` |
| `?x=1` | `?x=2` | `?x=2` | `?x=1&x=2` | `?x=1&x=2` | `?x=1` |
### urlfilter attributes
### $.urlfilter attributes
A `.urlfilter` class can use these attributes:
......@@ -68,7 +68,7 @@ The element on which `.urlfilter()` is called can have these attributes:
- `data-src=` changes which attribute holds the current URL when `data-target=` is a selector. Default: `src`
- `data-remove` is the same as specifying data-remove on every `.urlfilter`. Default: none
### urlfilter events
### $.urlfilter events
- `urlfilter` is fired on the source when the URL is changed. Attributes:
- `url`: the new URL
......@@ -76,7 +76,7 @@ The element on which `.urlfilter()` is called can have these attributes:
- `url`: the new URL
### urlfilter examples
### $.urlfilter examples
Add this line to the page:
......@@ -106,7 +106,7 @@ This activates all `.urlfilter` classes as below:
```html
<script type="text/html">Your platform is <%= navigator.userAgent %></script>
<script>
$('script[type="text/html"]').template()
$('script[type="text/html"]').template()
</script>
```
......@@ -131,13 +131,51 @@ You can pass data as `.template(data)`. The keys in `data` are available as vari
To re-render the template, run `.template()` again with different data.
The returned value has the rendered nodes as a jQuery object. For example:
## $.template attributes
Template containers can have an `src=` attribute that loads the template from a file:
```html
<script type="text/html" src="template.html"></script>
<script>
$('script[type="text/html"]').template()
</script>
```
If the `src=` URL returns a HTTP error, the HTML *inside* the script is rendered as
a template. The template can use:
- all data passed by the `$().template()` function, and
- an [xhr](http://api.jquery.com/Types/#jqXHR) object - which has error details
For example:
```html
<script type="text/html" src="missing.html">
Template returned error code: <%= xhr.status %>.
Data is <%= data %>
</script>
<script>
$('script[type="text/html"]').template({data: data})
</script>
```
### $.template events
- `template` is fired on the source when the template is rendered. Attributes:
- `templatedata`: the passed data
- `target`: the target nodes rendered, as a jQuery object
For example:
```js
$('script[type="text/html"]')
.template() // Returns nodes rendered from the template
.filter('div') // Filter all <div> elements inside
.attr('class', 'item') // Change their class
.on('template', function(e) { // Returns nodes rendered by the template
e.target // Get the target nodes
.filter('div') // Filter all <div> elements inside
.attr('class', 'item') // Change their class
})
.template() // Trigger the template AFTER binding the event handler
```
## $.dispatch
......@@ -320,6 +358,9 @@ g1.url.parse('/?a=1&b=2&c=3&d=4') // Update this URL
- `0.2.0`: Added
- [$.template](#template) renders lodash templates
- [L.TopoJSON](#ltopojson) loads TopoJSON files just like GeoJSON
- `0.2.1`:
- [$.template](#template) triggers a `template` event with the data and target nodes.
It also accepts a `src=` attribute that points to a template file.
# Release
......
var _template_fn = 'template.function',
_template_node = 'template.node'
var _renderer = 'template.render',
_target = 'template.target'
// Bind a template renderer to the node $this.data('template.render')
// The _renderer function
// - runs the html template, parses the result, and create a target node
// - appends the target node after $this (clearing any previous target nodess)
// - stores the target node in $this.data('template.target')
// - triggers a template event (with .templatedata, .target)
// - returns the target node
function make_template($this, html, data) {
var template = _.template(html)
function renderer(data) {
// Clear old target nodes if any
var $target = $this.data(_target)
if ($target)
$target.remove()
// Parse the template output and create a node collection
// $.parseHTML ensures that "hello" is parsed as HTML, not a selector
$target = $($.parseHTML(template(data))).insertAfter($this)
// Store the target node for future reference
$this.data(_target, $target)
// Trigger the template event
$this.trigger({type: 'template', templatedata: data, target: $target})
return $target
}
$this.data(_renderer, renderer)
return renderer(data)
}
export function template(data) {
// Pre-create the template rendering function
// Store this in .data('template.function')
this.each(function () {
var $this = $(this),
template
if (!$this.data(_template_fn)) {
template = _.template($this.html())
$this.data(_template_fn, function (data) {
var $target = $this.data(_template_node)
if ($target)
$target.remove()
// $.parseHTML ensures that "hello" is parsed as HTML, not a selector
$target = $($.parseHTML(template(data))).insertAfter($this)
$this.data(_template_node, $target)
return $target
})
}
})
// Return the template RESULT nodes, not the script nodes
var $result = $()
this.each(function () {
var result = $(this).data(_template_fn)(data)
$result = $result.add(result)
var $this = $(this)
var renderer = $this.data(_renderer)
// If there's no template function cached, cache it
if (!renderer) {
var src = $this.attr('src')
if (src) {
// If the AJAX load succeeds, render the loaded template
// Else render the contents, with an additional xhr variable
$.get(src)
.done(function(html) { make_template($this, html, data) })
.fail(function(xhr) {
make_template($this, $this.html(), _.extend({xhr: xhr}, data))
})
} else
// If no src= is specified, just render the contents
make_template($this, $this.html(), data)
} else
// If the renderer is already present, just use it
renderer(data)
})
return $result
return this
}
......@@ -14,12 +14,17 @@
<script type="text/html" id="t1">Your platform is <%= navigator.userAgent %></script>
<script>
function text(target) {
var node = target.get(0)
return node ? node.textContent.replace(/^\s+/, '').replace(/\s+$/, '') : ''
}
tape('$().template() renders plain text with variables', function(t) {
var $el = $('#t1').template()
t.equal($el.length, 1)
var text = $el.get(0).textContent.replace(/^\s+/, '').replace(/\s+$/, '')
t.equal(text, 'Your platform is ' + navigator.userAgent)
t.end()
t.plan(2)
$('#t1').one('template.g1', function(e) {
t.equal(e.target.length, 1)
t.equal(text(e.target), 'Your platform is ' + navigator.userAgent)
})
.template()
})
</script>
......@@ -29,18 +34,50 @@
<% }) %>
</script>
<script>
tape('$().template(data) passes data to the template', function(t) {
['x'].forEach(function(suffix) {
tape('$().template(data) passes data to the template', function (t) {
var suffixes = ['x', 'y']
t.plan(suffixes.length * 4)
suffixes.forEach(function (suffix) {
var list = ['a' + suffix, 'b' + suffix, 'c' + suffix]
var $el = $('#t2').template({ list: list })
var $divs = $el.filter('div')
t.equal($divs.length, list.length, 'Correct number of nodes are created')
var text = $divs.map(function () { return this.innerHTML }).get().join(' ')
t.equal(text, list.join(' '), 'Template content is correct')
$el.filter('div').attr('class', 'pass-data')
$('#t2').one('template', function (e) {
var $divs = e.target.filter('div')
t.equal($divs.length, list.length, 'Correct number of nodes are created')
var text = $divs.map(function () { return this.innerHTML }).get().join(' ')
t.equal(text, list.join(' '), 'Template content is correct')
t.deepEqual(e.templatedata, {list: list})
e.target
.filter('div')
.attr('class', 'item')
t.equal($('.item').length, 3, 'Repeated calls over-write the same node')
}).template({ list: list })
})
t.equal($('.pass-data').length, 3, 'Repeated calls over-write the same node')
t.end()
})
</script>
<script type="text/html" id="t3" src="sample-template.html"></script>
<script>
tape('$().template() renders src= via AJAX load', function(t) {
var data = { data: ['x', 'y'] }
$('#t3').one('template', function(e) {
t.equal(text(e.target), data.data.join(' '))
t.deepEqual(e.templatedata, data)
t.end()
}).template(data)
})
</script>
<script type="text/html" id="t4" src="nonexistent.html">
<%= xhr.status %>: Not found. <%= data.join(' ') %>
</script>
<script>
tape('$().template() renders contents if src= returns an error', function (t) {
var data = { data: ['x', 'y'] }
$('#t4').one('template', function(e) {
t.equal(text(e.target), '404: Not found. x y')
t.deepEqual(e.templatedata.data, data.data)
t.equal(e.templatedata.xhr.status, 404)
t.end()
}).template(data)
})
</script>
</body>
......
<%= data.join(' ') %>
const path = require('path')
const express = require('express')
const port = 1111
const paths = [
'test/jquery.urlfilter.html',
'test/jquery.dispatch.html',
'test/jquery.template.html',
'test/leaflet.topojson.html'
]
const port = process.argv.length <= 2 ? 1112 : 1111
const app = express()
.use(express.static(path.resolve(__dirname, '..')))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment