SLGrid - jQuery/Knockout 客户端组件






4.86/5 (4投票s)
SLGrid - 用于 CRUD 操作的 jQuery/Knockout 客户端组件
引言
SLGrid
是一个 jQuery/Knockout 客户端组件。它可以用于任何 Web 开发平台:ASP.NET/MVC、PHP、Java 等等,并且可以使用任何数据库系统。它具有标准的网格功能,例如排序、分页和过滤。SLGrid
使用 Knockout,它允许每个网格单元格可选地包含 Knockout 小部件,例如:DropDown
、日期选择器等,这些小部件绑定到 JavaScript 对象的列(字段)。许多 Knockout 小部件,例如:Money
、Percentage
、Image
等,可以在网上的 public
库中找到。对于 CRUD 操作,SLGrid
生成模板,这些模板应用于 Bootstrap 弹出框。
背景
SLGrid
是一个客户端组件,它使用 Ajax 调用与任何数据库系统通信。它使用自定义数据库适配器,例如:内存适配器、Ajax 适配器、LighStreamer 适配器等。
SLGrid
使用 Knockout,它通过声明性绑定将 UI 的各个部分连接到数据模型。
由于声明性绑定,相同的实体模型(文档)可以绑定到不同的视图,例如
- 行视图
- 行编辑视图
- 表单视图
- 表单编辑视图
这就是为什么数据行需要从网格中分离出来,并且数据行位于 ListView
中。
SLGrid
使用 Knockout,具有以下优点
- 网格单元格的更新速度非常快,因为网格单元格中包含的 DOM 元素已经绑定到 JavaScript 字段(列)。每次更改时无需
getDocumentById()
。 - 我们修改 JavaScript 数组:添加/更新/删除行,Knockout 会在网格中反映视觉变化。
- 每个单元格都可以包含可重用的小部件
将此库用作启动点,并根据您的需要对其进行自定义。例如,当用户单击行编辑按钮时,有几种显示详细信息表单的方法
- 在 Bootstrap 弹出框中显示表单
- 在行下面的展开面板中显示表单
- 在单独的页面中显示表单(使用编码的 URL 返回)
- ...
SLGrid
使用 JavaScript 的面向对象(继承)编程和一些设计模式,例如:模块模式等。用于 CRUD 功能的大量代码位于基类中。对于具有许多实体(管理模块)的 CRUD 操作非常有用。
JavaScript 原型继承
var SLEntity = function () {
this.whichTemplate = function() {
return this.displayMode == "edit" ? "person-edit-template" : "person-view-template"
}
}
var Person = function (mode) {
// instance properties
// method whichTemplate(), in base class SLEntity, uses displayMode property
this.displayMode = mode;
}
// Person extends SLEntity
Person.prototype = new SLEntity();
var person = new Person("edit");
alert(person.whichTemplate());
模块模式
模块模式克服了对象字面量的局限性,为变量和函数提供了隐私,同时公开了 public
API。在我们的例子中,只有 PersonList.viewModel
可以从外部访问。
function SLGridViewModel(configuration) {
this.itemsAtPage = ko.observableArray([]);
...
}
var PersonList = (function (DB) {
var db = DB;
var StorePerson = function (data) {
db.StorePerson(data)
}
var UpdatePerson = function (data) {
db.UpdatePerson(data)
}
function GridViewModel() {
...
}
GridViewModel.prototype = new SLGridViewModel();
return {
viewModel: new GridViewModel()
}
})(new PersonDB());
Using the Code
核心功能,从定义到规则和业务逻辑,都位于实体类中,例如 Person
。
定义类:Person、PersonList、PersonDB
通常,这些类由 SLGen
生成器生成为单独的 js 文件。
var Person = function (data) {
this.PersonId = ko.observable(data.PersonId || 0)
this.Name = ko.observable(data.Name || "")
this.TwitterName = ko.observable(data.TwitterName || "")
this.CityId = ko.observable(data.CityId || 0)
this.chosenCity = ko.computed(function () {
return CityList.getCity(this.CityId())
}, this);
// instance properties, functionality is into the base (Entity) class
this.isNew = ko.observable(data.isNew || false);
this.rowDisplayMode = ko.observable(data.isNew ? "rowAdd" : Person.DEFAULTS.rowDisplayMode);
}
// Person EXTENDS SLEntity
Person.prototype = new SLEntity();
Person.prototype.PersonId = ko.observable(0)
.extend({
primaryKey: true,
//headerText: "Id",
formLabel: "Id",
width: "100px",
defaultValue: function () { return this.getNextId() }
});
Person.prototype.Name = ko.observable("")
.extend({
headerText: "Name",
formLabel: "Name",
presentation: "bsPopoverLink", // view form bootstrap popover
width: "200px",
defaultValue: "",
required: true,
minLength: 2,
pattern: { message: 'Please, start with uppercase !', params: '^([A-Z])+' }
});
Person.prototype.TwitterName = ko.observable("")
.extend({
headerText: "Twitter",
formLabel: "Twitter name",
width: "auto", // one of the columns with take rest of row space
defaultValue: ""
});
Person.prototype.CityId = ko.observable(0)
.extend({
defaultValue: 101
});
Person.prototype.chosenCity = ko.observable()
.extend({
headerText: "City",
formLabel: "City",
width: "200px",
presentation: "bsSelectCity"
//presentation: "bsTypeaheadCity"
});
将视图模型添加到全局视图模型
var globalViewModel = {
...
cityList: CityList.viewModel,
personList: PersonList.viewModel
}
$(document).ready(function () {
ko.applyBindings(globalViewModel);
});
使用默认的 EntityListTemplate 或提供您的 PersonListTemplate
< script type="text/html" id="EntityListTemplate">
<div style="float:left">
<h4 data-bind="text:listHeader"></h4>
</div>
<div style="clear:both"></div>
<div style="width:100%">
<div data-bind="SLGrid: $data" />
<div>
<div style="float:left">
<button class="btn btn-primary btn-xs"
data-bind="click: add, enable: canAdd, text:textAdd"></button>
</div>
<div style="float:right;" data-bind="SLGridPager: $data">
</div>
<div style="float:right; margin-right:20px;">
<span data-bind='text: nItems'></span>
<span data-bind='text:textPager'></span>
<span data-bind="visible:maxPageIndex()>=itemsOnPager()">,
<span data-bind='text:maxPageIndex()+1'></span> pages</span>
</div>
<div style="clear:both"></div>
</div>
</div>
</script>
将模板绑定到 PersonList.viewModel
<div data-bind="template: {
name: 'EntityListTemplate',
data : personList,
afterRender: personList.afterRender }" >
</div>
Knockout 小部件
Knockout 支持创建自定义绑定。这可以控制 observables 如何与 DOM 元素交互,并为您提供很大的灵活性,以一种易于重用的方式封装复杂的行为。
SLGrid
单元格可以包含 Knockout 小部件,例如:DropDown、日期选择器等,以显示显示和编辑模式下的列(字段)值。这是一个显示以“.
”分隔千位的 float
值的 Widget 示例。
ko.bindingHandlers.bsMoney = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var mode = viewModel.rowDisplayMode()
var inRow = allBindings.get("inRow");
var isViewMode = inRow && viewModel.isRowViewMode() || !inRow && viewModel.isViewMode()
if (!isViewMode) {
var colName = $(element).data("field");
$(element).html("<input style='text-align:right'
class='form-control' data-field='" + colName
+ "' data-bind=\"value: " + colName +
"().toFixed(2), valueUpdate:'keyup'\">")
}
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = ko.utils.unwrapObservable(valueAccessor())
var mode = viewModel.rowDisplayMode()
var inRow = allBindings.get("inRow");
var isViewMode = inRow && viewModel.isRowViewMode() || !inRow && viewModel.isViewMode()
if (isViewMode) {
var tokens = value.toFixed(2).replace('-', '').split('.');
var s = '$' + $.map(tokens[0].split('').reverse(), function (elm, i) {
return [(i % 3 === 0 && i > 0 ? ',' : ''), elm];
}).reverse().join('') + '.' + tokens[1];
var markup = value < 0 ? '-' + s : s;
$(element).html(markup)
}
}
}
可以使用属性 presentation:"bsMoney"
轻松地将该小部件应用于字段。
Person.prototype.Amount = ko.observable()
.extend({
headerText: "Amount",
formLabel: "Amount",
defaultValue: 0,
presentation: "bsMoney",
align : 'right'
});
历史
- 2014 年 10 月 13 日:初始版本