使用 Spike & knockout.js Remotely远程数据绑定集合

分享于 

7分钟阅读

Web开发

  繁體

介绍

本文提供一个自定义控件,允许轻松创建一个的数据绑定 HTML的远程 table 的服务器的服务器:

  • 服务器上使用 ObservableCollection,以便向远程客户端通知更新的updates。
  • 它使用的knockout.js 库为客户端的客户端数据绑定服务提供收费。
  • 它在内部使用收费 websockets,但由引擎抽象,将回退到旧浏览器的Flash 套接字。
  • 它是跨平台和一个最小化的数据包负载和消息压缩。
  • 应用服务器是一个收费的自托管可执行,客户机只是一个的普通HTML文件。

[View a live demo]

背景

在写这篇文章之前几周,我想抽象客户端服务器网络,并创建一个可以自动填充的HTML table。 实际上,只要发生更改并从 Collection 中添加或者删除新项,就可以实时更新数据绑定视图。 这将允许人们建立无缝和非常容易的动态和非常好的网站,为用户提供了很好的。 本文介绍了我设计的一种方法:

  • 用于客户端数据绑定的Knockout.js
  • 客户端服务器通信的引擎

为了实现这一目的,我创建了一个名为 SyncList<T>同步列表的抽象,它继承了服务器的ObservableCollection,以及一个代表我们 SyncList <>的JavaScript视图,如图 below 所示:

使用代码

我希望这个API能够非常容易和直观地使用。 毕竟,复杂的现代网站包含许多集合,安装时间应该是最小且简单的。

首先,在服务器上,我们需要创建一个 SyncList Collection。 这里 Collection 应该足够聪明,可以传播更改本身。

var list = new SyncList<TestItem>("MyList"); 

我们需要先创建一个占位符 <div> 元素,该元素包含我们的table。

<divdata-bind='syncList: list1.gridViewModel'></div>

然后,需要将元素的 bind绑定到远程集合,并指定列,标题和各种版式属性:

var endpoint = new spike.ServerChannel("127.0.0.1:8002");var list1 = new SyncView({
 server: endpoint,
 name: "MyList",
 columns: [
 { headerText: "Id", rowText: "Id" },
 { headerText: "Name", rowText: "Name" },
 { headerText: "Packets In", rowText: "Incoming" },
 { headerText: "Packets Out", rowText: "Outgoing" },
 { headerText: "Time", rowText: "Time" },
 ],
 pageSize: 8});
ko.applyBindings(list1); 

这是实际上,集合是按名称绑定的,这个库将自动传播到服务器上 Collection的每个更改。 因为某些原因,感觉几乎是神奇的。

服务器端 SyncList

为了了解空间,我不会在本文中显示完整的服务器端实现,但是可以自由地探索代码。 然而,的中央类是 SyncList<T>,它本质上继承了 ObservableCollection,并通过 PubHub 向远程客户机转发了事件。

///<summary>/// Represents an observable list which is automatically synchronized with one/// or many remote clients.///</summary>///<typeparamname="T">The type of the element in the collection.</typeparam>publicclass SyncList<T> : ObservableCollection<T>, IDisposable
{
 privatereadonly PubHub Hub;
 privatereadonlystring Name;
 ///<summary>/// Constructs a new instance of <seecref="SyncList"/>.s///</summary>///<paramname="name">The name of the collection.</param>public SyncList(string name)
 {
 // Validateif (String.IsNullOrEmpty(name))
 thrownew ArgumentNullException("name");
 // Create a PubHubthis.Name = name;
 this.Hub = Service.Hubs.GetOrCreatePubHub(this.Name);
 // Hook observable collection eventsthis.CollectionChanged += OnCollectionChanged;
 this.Hub.ClientSubscribe += OnClientSubscribe;
 }
 ///<summary>/// Occurs when a new client have subscribed.///</summary>///<paramname="sender">The sender.</param>///<paramname="client">The client who have just subscribed.</param>privatevoid OnClientSubscribe(IHub sender, IClient client)
 {
 // Send everything this.Hub.PublishTo(new SyncListEvent(this.Items as IList), client);
 }
 ///<summary>/// Occurs when the colection is changed.///</summary>///<paramname="sender">The sender.</param>///<paramname="e">The event arguments.</param>privatevoid OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
 {
 // Publish the eventthis.Hub.Publish(new SyncListEvent(e));
 }
 (...)
}

另外,还有一些代码实现 IDisposable Pattern,以确保在不再需要 Collection 时清除所有内容。

客户端代码

在客户端,我们使用 ko.observableArray ( http://knockoutjs.com/documentation/observableArrays.html ),并相应地处理 ObservableCollection 事件。

this._server.on('connect', function () {
 self._server.hubSubscribe(self._name, null);
});// Make sure we have created an event objectif (this._server.hubEventInform == null) {
 this._server.hubEventInform = function (p) {
 $.event.trigger({
 type: "hubEvent",
 hubName: p.hubName,
 message: JSON.parse(p.message),
 time: new Date()
 });
 };
}// Attach a handler$(document).on("hubEvent", function (event) {
 if (self._name!= event.hubName)
 return;
 var value = event.message;
 if (value.Action == "Reset") {
 self.clear();
 }
 if (value.Action == "Remove") {
 self.removeAt(value.OldIndex);
 }
 if (value.Action == "Replace") {
 self.replace(value.NewIndex, value.NewItems[0]);
 }
 if (value.Action == "Move") {
 self.move(value.OldIndex, value.NewIndex);
 }
 if (value.Action == "Add" || value.Action == "Reset") {
 if (value.NewItems!= null) {
 value.NewItems.forEach(function (item) {
 self.add(item);
 });
 }
 }
}); 

我还在客户端创建了一个定制控件,这是由网站上的页面样例 inspired。 所有实现都附加到了本文的zip文件中,请查看 !

历史记录

  • 1 - 源代码文章更新为 Spike v3.0"
  • 12/03/2014源代码更新至 Spike v2.3
  • 14/09/2013文章

数据  COL  Collect  collection  SPI  databind  
相关文章