...
 
Commits (3)
# Change log
- `0.16.2`:
- Fixes a bug in [$.formhandler](docs/formhandler.md) timezone handling
- `0.16.1`:
- Fixes a bug in [$.template](docs/template.md) `.template('dispose')`
- `0.16.0`:
- [$.formhandler](docs/formhandler.md) supports `{link: false}` to clear
links in cells, and `{link: '?col=val'}` to allow any cell to define any
custom filters.
- [g1.fuzzyseach](docs/fuzzysearch.md) filters the text using fuzzy logic.
This is useful for search-as-you-type.
- [$.template](docs/template.md) supports a `.template('dispose')` option
that clears the last rendered template output.
- `0.15.0`:
- [$.formhandler](docs/formhandler.md) now supports client-side validation
and sorting by multiple columns. A bug related to encoding special
......
......@@ -61,6 +61,7 @@ The full list of options is below. Simple options can be specified as `data-` at
- `{'', 'Equals...', '!', 'Does not equal...', ...}`. The default list of operators is based on the auto-detected type of the column.
- `link`: string / function that generates a link for this each cell.
- If no `link:` is specified, clicking on the cell filters by that cell.
- If `link: false`, the cell has no link
- If `link:` is a function, opens a new window with the URL as `fn(args)`.
- function accepts an object with these keys:
- `name`: column name
......@@ -70,8 +71,11 @@ The full list of options is below. Simple options can be specified as `data-` at
- `row`: row data
- `data`: the dataset from `src`
- Example: `function(args) { return 'https://example.org/city/' + args.value }`
- If `link:` is a string, opens a new window with the string URL interpolated as a lodash template with an object (mentioned above) as data.
Example: `"https://example.org/city/<%- value >"`
- Example: `function(args) { return false }` clears the link
- If `link:` is a URL with a path, opens a new window with the string
URL interpolated as a lodash template with an object (mentioned above)
as data. Example: `"https://example.org/city/<%- value >"`
- If `link:` is a URL with only query parameters, apply the filters. Example: `"?name=<%- row.name %>"`
- `hideable`: `true` (default) / `false`. Show or hide `Hide` option in header dropdown
- `hide`: `true` / `false` (default). Hides the column
- `unique`: TODO: {dict of query parameter and display value} or [list of values] or function?
......@@ -96,11 +100,11 @@ The full list of options is below. Simple options can be specified as `data-` at
- `false`: disables the table (and shows nothing for the main content)
- `count`: Shows the number of rows. Can be `true` (default) / `false`
- `page`: Shows the page control. Can be `true` (default) / `false`.
- `pageSize`: page size. Defaults to 100
- `pageSize`: page size (or via `data-page-size`). Defaults to 100
- `size`: Shows the page size control. Can be `true` (default) / `false`
- `sizeValues`: Allowed page size values. Defaults to `[10, 20, 50, 100, 500, 1000]`
- `sizeValues`: Allowed page size values (or via `data-size-values`). Defaults to `[10, 20, 50, 100, 500, 1000]`
- `export`: Shows the export control. Can be `true` (default) / `false`
- `exportFormats`: {xlsx: 'Excel'}
- `exportFormats`: Defines export formats to use (or via `data-export-formats`). E.g. `{xlsx: 'Excel', 'csv': 'CSV'}`
- `filters`: Shows the applied filters control. Can be `true` (default) / `false`
- `transform`: an optional function() that modifies data. It accepts a dict that has keys:
- `data`: the FormHandler data
......
# g1.fuzzysearch
`g1.fuzzysearch(data, options)` returns a fuzzy search function that filteres
`g1.fuzzysearch(data, options)` returns a fuzzy search function that filters
the data based on the text.
For example:
......
......@@ -135,7 +135,6 @@ $('script.list')
</script>
```
## $.template external source
Template containers can have an `src=` attribute that loads the template from a file:
......@@ -197,11 +196,36 @@ You can also use the `selector: ...` option. For example:
```
## $.template dispose
Deletes the output of a rendered template, use `$('...').template('dispose')`.
This removes the DOM elements last created by the template.
This is useful for deleting error messages or previous output that's no longer
relevant.
```js
$.getJSON('data').then(function (data) {
// When we get data, show the dashboard, dispose any previous error
$('script.dashboard').template({ data: data })
$('script.error').template('dispose')
}).fail(function (xhr, error, msg) {
// If there's an error, dispose any previous dashboard, show the error
$('script.dashboard').template('dispose')
$('script.error').template({ error: msg })
})
```
When [appending](#template-append), this only deletes the last rendered
template. But this behavior may change.
## $.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
When a template is rendered or disposed, `template` is fired on the source. Attributes:
- `templatedata`: the passed data argument (e.g. `{...}`, `'dispose'`)
- `target`: the target nodes rendered or disposed (if any), as a jQuery object
For example:
......@@ -213,7 +237,7 @@ For example:
<script>
$('script.event')
.on('template', function(e) { // When the template is rendered,
$(e.target).find('.data') // find the <pre> tag inside target nodes
e.target.find('.data') // find the .data class inside target nodes
.html(JSON.stringify(e.templatedata)) // and enter the template data
})
.template({x: 1}) // Trigger template AFTER .on('template')
......
......@@ -7,7 +7,7 @@ Suppose we have buttons that sort based on "City" or "Sales". These are usually
bound directly to DOM events like this:
```html
<!-- DO NOT DO THIS -- this example shows what should NOT be done -->
<!-- DO NOT DO THIS -- this example shows a BAD practice -->
<button class="sort">City</button>
<button class="sort">Sales</button>
<script>
......@@ -32,8 +32,8 @@ Now, changes in the URL should trigger actions:
```js
$(window)
.urlchange() // URL changes trigger '#?city' events
.on('#?city', function(e, city) { action(city) })
.on('#?city', function(e, city) { action(city) }) // Listen to #? events
.urlchange() // Enable these events
```
Examples:
......
{
"name": "g1",
"version": "0.15.0",
"version": "0.16.2",
"description": "Gramex 1.x interaction library",
"license": "MIT",
"author": "S Anand <s.anand@gramener.com>",
......
......@@ -95,23 +95,24 @@ export function formhandler(js_options) {
meta.columns = data.length ? _.map(data[0], function (val, col) { return { name: col } }) : []
// If any column name is '*', show all columns
var star_col = _.find(options.columns, function (o) { return o['name'] === '*' })
if (star_col) {
var action_header_cols = _.cloneDeep(meta.columns)
_.map(options.columns, function (option_col) {
var found = _.find(meta.columns, function (o) { return o['name'] === option_col.name })
if (!found && option_col.name !== '*')
action_header_cols.push(option_col)
var star_col_opts = _.find(options.columns, { name: '*' })
if (star_col_opts) {
var column_opts_clone = []
options.columns.forEach(function (col_opts) {
if (col_opts.name === '*') {
_.differenceBy(meta.columns, options.columns, 'name')
.forEach(function (meta_col_opts) {
column_opts_clone.push(Object.assign({}, star_col_opts, meta_col_opts))
})
} else {
column_opts_clone.push(col_opts)
}
})
action_header_cols = _.map(action_header_cols, function (col) {
var options_col = _.find(options.columns, function (o) { return o['name'] === col.name })
return options_col ? options_col : $.extend({}, star_col, col)
})
options.columns = _.cloneDeep(column_opts_clone)
}
options.columns = action_header_cols ? action_header_cols : options.columns
// Render all components into respective targets
var template_data = {
data: data,
......@@ -141,12 +142,17 @@ export function formhandler(js_options) {
var url_args = parse(location.hash.replace(/^#/, '')).searchList
url_args = namespace(url_args, options.name)
// Create arguments passed to the FormHandler. Override with the user URL args
var args = _.extend({
c: options.columns.map(function (d) { return d.name }),
var default_url_args = {
_limit: options.pageSize,
_format: 'json',
_meta: 'y'
}, url_args)
}
var star_col = _.find(options.columns, function (o) { return o['name'] === '*' })
if (!star_col) default_url_args['_c'] = options.columns.map(function (d) { return d.name })
var args = _.extend(default_url_args, url_args)
$('.loader', $this).removeClass('d-none')
function done(data, status, xhr) {
......@@ -372,6 +378,9 @@ function actionHandler($this, options, template) {
var arg = {
row: $(this).closest('[data-val]').data('val'),
index: $(this).closest('[data-row]').data('row'),
this: $(this),
options: options,
templates: default_templates,
notify: notify.bind(this, $this, template)
}
var action = $(this).data('action')
......
......@@ -140,27 +140,34 @@ Each template receives these variables:
fmt === "string" && colinfo.type === "number" ?
numeral(val).format(colinfo.format) :
fmt === "string" && colinfo.type === "date" ?
moment(val).format(colinfo.format):
moment.utc(val).format(colinfo.format):
val,
col_link
%>
<% if (!isEdit && 'link' in colinfo) var col_link = typeof colinfo.link == 'function' ? colinfo.link({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data}) : _.template(colinfo.link)({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data}) %>
<% if (colinfo.template) { %>
<%= typeof colinfo.template == 'function' ? colinfo.template({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data}) : _.template(colinfo.template)(({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data})) %>
<% } else if (col_link) { %>
<td>
<a href="<%- col_link %>" target="_blank">
<%= disp %>
</a>
</td>
<% if (!isEdit && colinfo.template) { %>
<%= typeof colinfo.template == 'function' ?
colinfo.template({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data}) :
_.template(colinfo.template)(({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data})) %>
<% } else if (!isEdit && 'link' in colinfo) { %>
<% col_link = typeof colinfo.link == 'function' ?
colinfo.link({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data}) :
(typeof colinfo.link == 'string' ?
_.template(colinfo.link)({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data})
: colinfo.link)
%>
<% if (col_link === false) { %>
<td data-key="<%- colinfo.name %>" data-value="<%- val %>"><%= disp %></td>
<% } else if (col_link && col_link[0] == '?') { %>
<td data-key="<%- colinfo.name %>" data-value="<%- val %>"><a class="urlfilter" href="<%- col_link %>"><%= disp %></a></td>
<% } else { %>
<td data-key="<%- colinfo.name %>" data-value="<%- val %>"><a href="<%- col_link %>" target="_blank"><%= disp %></a></td>
<% } %>
<% } else if (isEdit && isEditable) { %>
<td data-key="<%- colinfo.name %>">
<td data-key="<%- colinfo.name %>" data-value="<%- val %>">
<%= _.template(templates['template_editable'])({isEditable: isEditable, val: val}) %>
</td>
<% } else { %>
<td>
<td data-key="<%- colinfo.name %>" data-value="<%- val %>">
<a class="urlfilter" href="?<%- encodeURIComponent(colinfo.name) %>=<%- encodeURIComponent(val) %>&amp;_offset=">
<%= disp %>
</a>
......@@ -175,6 +182,11 @@ Each template receives these variables:
<!-- end -->
<!-- var template_editable -->
<% if (!isEditable) { %>
<a class="urlfilter" href="?<%- val %>=<%- val %>&amp;_offset=">
<%= val %>
</a>
<% } %>
<% if (isEditable.input == 'select') { %>
<select
......@@ -185,7 +197,7 @@ Each template receives these variables:
>
<option value="" disabled selected>-- select --</option>
<% _.each(isEditable.options, function(item) { %>
<option <%- val && val === item ? 'selected': null %> value="<%- item %>">
<option <%- val !== undefined && val === item ? 'selected': null %> value="<%- item %>">
<%- item %>
</option>
<% }) %>
......@@ -195,7 +207,7 @@ Each template receives these variables:
<% } else if (isEditable.input == 'radio') { %>
<% _.each(isEditable.options, function(item) { %>
<input type="radio" <%- val && val === item ? 'checked': null %> value="<%- item %>"
<input type="radio" <%- val !== undefined && val === item ? 'checked': null %> value="<%- item %>"
<% for (key in isEditable.attrs) { %>
<%= key + '="' + isEditable.attrs[key] + '"' %>
<% } %>
......@@ -388,13 +400,21 @@ Each template receives these variables:
val = row[colinfo.name],
disp = (fmt == "function" ? colinfo.format({index: rowIndex, name: colinfo.name, value: val, row: row, data:data }) :
fmt === "string" && colinfo.type === "number" ? numeral(val).format(colinfo.format) :
fmt === "string" && colinfo.type === "date" ? moment(val).format(colinfo.format) :
fmt === "string" && colinfo.type === "date" ? moment.utc(val).format(colinfo.format) :
val) %>
<div>
<strong><%= colinfo.name %></strong>:
<% if ('link' in colinfo) {
var col_link = typeof colinfo.link == 'function' ? colinfo.link({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) : _.template(colinfo.link)({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) %>
<% if ('link' in colinfo) { %>
<% var col_link = typeof colinfo.link == 'function' ?
colinfo.link({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) :
_.template(colinfo.link)({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) %>
<% if (col_link === false) { %>
<%= disp %>
<% } else if (col_link && col_link[0] == '?') { %>
<a class="urlfilter" href="<%- col_link %>"><%= disp %></a>
<% } else { %>
<a href="<%- col_link %>" target="_blank"><%= disp %></a>
<% } %>
<% } else { %>
<a class="urlfilter" href="?<%- encodeURIComponent(colinfo.name) %>=<%- encodeURIComponent(val) %>&amp;_offset=">
<%= disp %>
......
......@@ -8,6 +8,17 @@ export function template(data, options) {
// Store this in .data('template.function')
findall(this, selector).each(function () {
var $this = $(this)
// If we want to dispose the last target, just dispose it.
if (data === 'dispose') {
var $oldtarget = $this.data(_prev_created)
if ($oldtarget)
$oldtarget.remove()
return $this.trigger({
type: 'template',
templatedata: data,
target: $oldtarget
})
}
var renderer = $this.data(_renderer)
// If there's no template function cached, cache it
if (!renderer) {
......@@ -32,7 +43,7 @@ export function template(data, options) {
}
var _renderer = 'template.render'
var _prev_target = 'template.prev_target'
var _prev_created = 'template.prev_created'
// Bind a template renderer to the node $this.data('template.render')
// This renderer function accepts (data, options) and creates
......@@ -43,7 +54,7 @@ var _prev_target = 'template.prev_target'
// - returns the target node
function make_template($this, html, data, default_options) {
var compiled_template = _.template(html)
var $target
var $created
function renderer(data, options) {
html = compiled_template(data)
// Get options. DOM data-* over-rides JS options
......@@ -54,23 +65,24 @@ function make_template($this, html, data, default_options) {
engine = template.engines[engine] || template.engines['default']
// If we're appending the contents, just add the text
if (append) {
$created = $($.parseHTML(html))
// If we're appending to a target node, just append to it.
if (target)
$(target).append($($.parseHTML(html)))
$(target).append($created)
// If no target node, add BEFORE template. Future appends will be in sequence
else
$this.before($($.parseHTML(html)))
$this.before($created)
}
// If we're not appending, replace the contents using the renderer
else {
// The engine must return the target nodes. See template.engines spec below
$target = engine($this, target, html)
// Store the target nodes for future reference. See template.engines spec below
$this.data(_prev_target, $target)
// The engine must return the created nodes. See template.engines spec below
$created = engine($this, target, html)
// Store the created nodes for future reference. See template.engines spec below
$this.data(_prev_created, $created)
}
// Trigger the template event. Use "templatedata" since ".data" is reserved
$this.trigger({ type: 'template', templatedata: data, target: $target })
return $target
$this.trigger({ type: 'template', templatedata: data, target: $created })
return $created
}
$this.data(_renderer, renderer)
return renderer(data)
......@@ -82,9 +94,9 @@ function make_template($this, html, data, default_options) {
// $this: the <script> element
// target: the target selector or node to render into. May be undefined
// html: the HTML to render at the target (or around $this if target is missing)
// It returns the target nodes created as a jQuery object. This is used in 2 ways:
// It returns the created nodes as a jQuery object. This is used in 2 ways:
// - the template event.target attribute is this return value
// - $this.data(_prev_target) is set to this return value
// - $this.data(_prev_created) is set to this return value
template.engines = {}
// The default engine uses jQuery
......@@ -97,7 +109,7 @@ template.engines['default'] = template.engines['jquery'] = function ($this, targ
$(target).html($target)
else {
// Remove any previous targets and re-create the output
var $oldtarget = $this.data(_prev_target)
var $oldtarget = $this.data(_prev_created)
if ($oldtarget)
$oldtarget.remove()
$this.before($target)
......@@ -108,7 +120,7 @@ template.engines['default'] = template.engines['jquery'] = function ($this, targ
/* globals morphdom */
template.engines['vdom'] = function ($this, target, html) {
// If no target is specified, use the previous target, if any
target = target || $this.data(_prev_target)
target = target || $this.data(_prev_created)
// If a target is specified, wrap the HTML with the target node.
// For example, <div id="target">...</div> will wrap the HTML with
// <div id="target"></div>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -34,6 +34,8 @@
</head>
<body>
<div class="no-link" data-src="/formhandler-data"></div>
<div class="row_template1" data-src="/formhandler-data"></div>
<div class="delete_btn" data-src="/formhandler-data"></div>
<div class="test-star-without-title" data-src="/formhandler-data"></div>
<div class="test-star-with-title" data-src="/formhandler-data"></div>
......@@ -43,18 +45,19 @@
<div class="row_template4" data-src="/formhandler-data"></div>
<div class="row_template3" data-src="/formhandler-data"></div>
<div class="row_template2" data-src="/formhandler-data"></div>
<div class="row_template1" data-src="/formhandler-data"></div>
<script>
tape('$() test delete action for empty data case and test for edit and add button', function(t) {
$('.delete_btn').formhandler({
tape('$() test link false renders just td tag, without a tag', function(t) {
$('.no-link').formhandler({
columns: [
{
name: '*'
name: 'Continent',
link: '?ID=AND',
},
{
name: 'ID',
link: false,
editable: false
},
{
......@@ -68,6 +71,46 @@
]
}
},
{
name: '*'
}
],
pageSize: 3,
onhashchange: false,
add: true,
edit: true
})
.on('load', function () {
t.equals($('div.no-link tr:nth-child(1) > td:nth-child(2)').text().trim(), "AND")
t.equals($('div.no-link tr:nth-child(2) > td:nth-child(2)').text().trim(), "ARE")
t.ok($('div.no-link tr:nth-child(1) > td:nth-child(1) a').hasClass('urlfilter'), 'has class urlfilter')
t.ok($('div.no-link tr:nth-child(1) > td:nth-child(1) a').attr('href'), '?ID=AND')
t.end()
})
})
tape('$() test delete action for empty data case and test for edit and add button', function(t) {
$('.delete_btn').formhandler({
columns: [
{
name: 'ID',
editable: false
},
{
name: 'Stripes',
editable: {
input: 'select', // renders a default select dropdown as <select class="form-control form-control-sm">...</select>
options: [ // `options` is mandatory because `input` is "select"
'Vertical',
'Horizontal',
'Diagonal'
]
},
},
{
name: '*'
},
{
name: 'DELETE Action',
template: function() {
......@@ -88,9 +131,9 @@
$('.add-btn').click()
t.ok($('.edit-btn').prop('disabled'))
// stripes column is dropdown select HTML element and not input textbox
t.equals($('div.formhandler tr.new-row td:nth-child(6) select').length, 1)
t.equals($('div.delete_btn tr.new-row td:nth-child(2) select').length, 1)
// all other columns must be input textbox and editable, overriding isEditable: false option also
t.equals($('div.formhandler tr.new-row td:nth-child(1) input').length, 1)
t.equals($('div.delete_btn tr.new-row td:nth-child(1) input').length, 1)
$('.add-btn').click()
// on empty row click, .new-row element does not exist
t.equals($('.new-row', $('.delete_btn')).length, 0)
......@@ -148,6 +191,7 @@
t.equals($('body > div.row_template1 > div.position-relative > div.formhandler > div.table > table > tbody > tr:nth-child('+(index+1)+') > td:nth-child(3)').text(), ''+result[index]['c2'])
t.equals($('body > div.row_template1 > div.position-relative > div.formhandler > div.table > table > tbody > tr:nth-child('+(index+1)+') > td:nth-child(4)').text(), ''+result[index]['Continent'])
}
console.log("$$$$$$$$$$$$$$$$$$$$$$$ ")
t.end()
});
})
......
......@@ -271,7 +271,7 @@
})
.on('load', function () {
// no. of columns in meta data must be same as rendered columns
t.equal($('.fh6 thead tr th').length, 18)
t.equal($('.fh6 thead tr th').length, 19)
// there should not be a column with name 'Continent'
t.notOk($('.fh6 thead tr').text().match(/Continent/))
// there shoudl be a column with name 'CustomTitle'
......@@ -295,14 +295,14 @@
.on('load', function () {
current_class_count += 1
// no. of columns in meta data must be same as rendered columns
t.equal($('.test_star_overrride thead tr th').length, 18 * current_class_count)
t.equal($('.test_star_overrride thead tr th').length, 19 * current_class_count)
// there should not be a column with name 'Continent'
t.notOk($('.test_star_overrride thead tr').text().match(/Continent/))
// there shoudl be a column with name 'CustomTitle'
t.ok($('.test_star_overrride thead tr').text().match(/CustomTitle/))
t.equal($('.test_star_overrride thead tr').text().match(/CustomTitle/).length, 1)
// Rest of 16 columns must have title as 'SameName'
t.equal($('.test_star_overrride thead tr').text().split('SameName').length - 1, 17 * current_class_count)
t.equal($('.test_star_overrride thead tr').text().split('SameName').length - 1, 18 * current_class_count)
// column is hideable by default
t.equal($('.test_star_overrride [data-col="Continent"] div:nth-child(1) div a:last-of-type').text().trim(), "Hide".repeat(current_class_count))
})
......@@ -1075,7 +1075,7 @@
})
.on('load', function () {
init_class_count += 1
t.ok($('.test-star-without-title table thead th a.dropdown-toggle').length == 18 * init_class_count)
t.ok($('.test-star-without-title table thead th a.dropdown-toggle').length == 19 * init_class_count)
})
})
......
......@@ -157,6 +157,7 @@
})
</script>
<!-- Renders to target DOM element -->
<script class="target-dom" data-target=".target1" type="text/html">1 + 1 = <%= 1 + 1 %></script>
<script class="target-dom" type="text/html">2 + 2 = <%= 2 + 2 %></script>
<div class="target1"></div>
......@@ -176,42 +177,84 @@
})
</script>
<!-- Appends with data-append even if { append: false } -->
<script class="append-dom" data-append="true" type="text/html"><i class="new-dom"><%= n %></i><i class="new-dom"><%= n %></i></script>
<script class="append-dom" data-append="true" data-target=".append-dom-target" type="text/html"><i class="new-dom-target"><%= n %></i><i class="new-dom-target"><%= n %></i></script>
<div class="append-dom-target"></div>
<script>
tape('$().template() with data-append="true" appends data to target', function (t) {
var count = 5
for (var i = 0; i < count; i++) {
$('script.append-dom').template(
{ n: i },
{ append: false } // append=false is over-ridden by data-append
)
}
var count = 3
t.plan(count * 4 + 2) // 2 tests at the end, plus 2 .on('template') tests x 2 DOM elements
_.range(count).forEach(function (i) {
$('script.append-dom')
.one('template', function (e) {
t.equals(+text(e.target), i)
t.deepEqual(e.templatedata, { n: i })
})
.template(
{ n: i },
{ append: false } // append=false is over-ridden by data-append
)
})
t.equal($('.new-dom').length, count * 2)
t.equal($('.append-dom-target .new-dom-target').length, count * 2)
t.end()
})
</script>
<!-- Appends with { append: true } -->
<script class="append-js" type="text/html"><i class="new-js"><%= n %></i><i class="new-js"><%= n %></i></script>
<script class="append-js" data-target=".append-js-target" type="text/html"><i class="new-js-target"><%= n %></i><i class="new-js-target"><%= n %></i></script>
<div class="append-js-target"></div>
<script>
tape('$().template() with {append:true} option appends data to target', function (t) {
var count = 5
for (var i = 0; i < count; i++) {
$('script.append-js').template(
{ n: i },
{ append: true }
)
}
var count = 3
t.plan(count * 4 + 2) // 2 tests at the end, plus 1 .on('template') tests x 2 DOM elements
_.range(count).forEach(function (i) {
$('script.append-js')
.one('template', function (e) {
t.equals(+text(e.target), i)
t.deepEqual(e.templatedata, { n: i })
})
.template(
{ n: i },
{ append: true }
)
})
t.equal($('.new-js').length, count * 2)
t.equal($('.append-js-target .new-js-target').length, count * 2)
t.end()
})
</script>
<!-- Disposes output -->
<div class="dispose-root">
<script type="text/html" class="dashboard"><p class="dashboard"><%- text %></p></script>
<div class="dispose-target">Old content deleted</div>
</div>
<script>
tape('$().template("dispose") disposes rendered templates', function (t) {
t.plan(7)
// Disposing an un-created template does not raise an error
$('.dispose-root script.dashboard').template('dispose')
// Create the template
$('.dispose-root').one('template', function (e) {
t.equal($('.dispose-root p.dashboard').length, 1)
}).template({text: 'Dashboard'})
// Dispose it. It should not exist. It should trigger a template events
$('.dispose-root script.dashboard').one('template', function (e) {
t.equal($('.dispose-root p.dashboard').length, 0)
t.equal(e.templatedata, 'dispose')
t.ok(e.target.is('p.dashboard'))
}).template('dispose')
// If data-target is specified, the contents are removed but not the target
$('.dispose-root script.dashboard').one('template', function (e) {
t.ok(e.target.parent().is('.dispose-target'))
t.equal(text(e.target), 'Dashboard')
}).template({ text: 'Dashboard' }, { target: '.dispose-target' })
$('.dispose-root script.dashboard').one('template', function (e) {
t.equal($('.dispose-target').html(), '')
}).template('dispose')
})
</script>
<h1>Morphdom templates</h1>
<div class="morphdom-t1">
......@@ -289,5 +332,6 @@
}, 0)
})
</script>
</body>
</html>