JsViews Maps: Sorted Table Grid Views

This sample will probably be added to jsviews.com

by BorisMoore

HTML

<script src="//www.jsviews.com/download/jsviews.js"></script>
<link rel="stylesheet" href="//www.jsviews.com/samples/samples.css">
<div id="page"></div>

<script type="text/x-jsrender" id="tmpl">
	<h4>View one</h4>
	{^{pager rows cols pageLength=12 page=1 sortby='role'}}
		<div class="pagerHeader one">
			{^{pager_header/}}
		</div>
		<table class="grid">
			<tbody><tr>
				{^{for ~tag.cols}}
					<td data-link="{on ~tag.sort #index}">
						{{:label}}
						{^{if ~tag.options.sortby === field}}
							{{!--<img data-link="src{:~tag.options.reverseSort ? 'images/up.png' : 'images/down.png'}" />--}}
							<span data-link="~tag.options.reverseSort ? '^' : 'v'" ></span>
						{{/if}}
					</td>
				{{/for}}
				<td></td>
			</tr></tbody>
			<tbody>
				{^{pager_rows}}
					<tr>
						{^{props}}
							<td><input data-link="prop" /></td>
						{{/props}}
						<td><button class="delete" data-link="{on ~parentTags.pager.remove #index}"></button></td>
					</tr>
				{{/pager_rows}}
				{^{pager_add_row}}
					<tr class="addRow">
						{^{props}}
							<td><input data-link="{:prop:} {on 'keydown' ~parentTags.pager.insert}" /></td>
						{{/props}}
						<td><button data-link="{on ~parentTags.pager.add}">add</button></td>
					</tr>
				{{/pager_add_row}}
			</tbody>
		</table>
	{{/pager}}

	<h4>View two</h4>
	{^{pager rows cols pageLength=2 page=1 sortby='name'}}
		<div class="pagerHeader two">
			{^{pager_header/}}
		</div>
		<table class="grid">
			<tbody><tr>
				{^{for ~tag.cols}}
					<td data-link="{on ~tag.sort #index}">
						{{:label}}
						{^{if ~tag.options.sortby === field}}
							{{!--<img data-link="src{:~tag.options.reverseSort ? 'images/up.png' : 'images/down.png'}" />--}}
							<span data-link="~tag.options.reverseSort ? '^' : 'v'" ></span>
						{{/if}}
					</td>
				{{/for}}
				<td></td>
			</tr></tbody>
			<tbody>
				{^{pager_rows}}
					<tr>
						{^{props}}
							<td><input data-link="prop" /></td>
						{{/props}}
						<td><button...

CSS

.pagerHeader input {
	width: 30px;
}
.pagerHeader select {
	width: 60px;
}
.pagerHeader input[type=checkbox] {
	width: auto;
}
.pagerHeader .nav {
	width: 20px;
	height: 20px;
	padding: 0;
}
.delete:before {
	content: "\00d7";
	color: red;
}

JavaScript

var rowPagerMap = $.views.map({
		getTgt: function(rows, options) {
			if (options.sortby) {
				rows = rows.slice().sort(function(a, b) {
					return (options.reverseSort ? -1 : 1) * a[options.sortby].toLowerCase().localeCompare(b[options.sortby].toLowerCase());
				});
			}
			var pageLen = parseInt(options.pageLength),
				start = parseInt(options.page) * pageLen - pageLen,
				end = start + pageLen;
			return rows.slice(start, end);
		}
	});

$.views.tags({
	pager: {
		init: function() {
			this.rows = this.tagCtx.args[0];
			this.cols = this.tagCtx.args[1];
			this.options = this.tagCtx.props;
			this.rowToAdd = this.createRow();
			this.blankRow = this.createRow();
			this.map = rowPagerMap.map(this.rows, this.options);
			this.updatePage();
		},
		insert: function(ev) {
			if (ev.which === 13) {
				// On Enter, insert
				this.add();
			}
		},
		add: function() {
			$.observable(this.rows).insert(this.createRow(this.rowToAdd));
			$.observable(this.rowToAdd).setProperty(this.blankRow);
		},
		remove: function(index) {
			$.observable(this.rows).remove($.inArray(this.map.tgt[index], this.rows));
			// (Alternatively: $.inArray(eventArgs.view.data, this.rows) - but that would break if you use itemVar=...)
		},
		page: function(page) {
			$.observable(this.options).setProperty("page", parseInt(page));
		},
		prevPage: function() {
			this.page(this.options.page - 1);
		},
		nextPage: function() {
			this.page(this.options.page + 1);
		},
		sort: function(index) {
			var col = this.cols[index].field;
			// (Alternatively: col = eventArgs.view.data.field; - but that would break if you use itemVar=...)
			$.observable(this.options).setProperty({
				sortby: col,
				reverseSort: this.options.sortby === col ? !this.options.reverseSort : false
			});
		},

		updatePage: function() {
			$.observable(this).setProperty("isMaxPage", this.options.page * this.options.pageLength >= this.map.src.length);
			$.observable(this).setProperty("isMinPage", this.options.page <=...