コンテンツへスキップ
jQuery Grid、WebSockets、KnockoutJSによるライブデータ

jQuery Grid、WebSockets、KnockoutJSによるライブデータ

効果的または機能的なアプリケーションを持つことだけを目的として、ライブデータのように、クライアントをリアルタイムで更新するライブが唯一の方法であると思われる場合、多くのシナリオがあります。

11min read

効果的または機能的なアプリケーションを持つことだけを目的として、ライブデータのように、クライアントをリアルタイムで更新するライブが唯一の方法であると思われる場合、多くのシナリオがあります。

それが物事のあり方であるように感じます。ユーザー自身は、動的なWebアプリケーションにかなり慣れています。あなたの想像力を働かせるために、以下に示されたアプリケーションのスクリーンショットを以下に示します(この静けさは実際にはそれを正当化しないことを覚えておいてください)。

グリッドは、3つのアイテムの更新を受信したとき。

ただし、このようなアプリを配信する機能は、jQuery ウィジェットなどのクライアント側のロジックや、サーバーからのヘルプ (つまり通信) に依存しています。 それを表す正しい用語は「ポーリング」(ウィキペディアで詳細を読むにはこれに従ってください)であり、クライアントがサーバーに「まだ何か新しいものはありますか?」と尋ねるプロセスです。そして、それは何度も繰り返されます。問題は、余分なトラフィックを生成し、サーバーが「いいえ」と言うためだけに大量のリクエストを処理するようになることです。それは、プッシュ技術(または簡単に言えば、通知)によって過去のものになるかもしれません(モバイルデバイスを見る)。モバイル OS はこのようなサービスをサポートしており (Apple やWindows Phoneではプッシュ通知と呼び、Android ではクラウドからデバイスへのメッセージングと呼んでいます)、いくつかの手法 (接続を閉じずにループで開いたままにする HTML ストリーミングなど) があります。それから、また...。

WebSockets

WebSocketは、全二重、つまりノンブロッキングの同時双方向通信を提供するWebテクノロジーです。もちろん、APIが付属しており、クライアントとサーバーの両方で使用できます。プロトコル自体にはいくつかの変更が加えられ、IETFによってRFC 6455として標準化されています。1つのTCP接続(1つのソケット)で2方向の転送を維持できる機能は、接続数を減らすことができるため、非常に魅力的です。そして、はい、上記で説明した問題に続いて、WebSocketプロトコルは、データを要求またはポーリングすることなく、クライアントにデータを送信する手段をサーバーに提供できます。これは、このブログでお見せしたいことです - このプロトコルを2つの一般的なクライアントコントロールと一緒に素晴らしいアプリにまとめる方法です。現在、プロトコルは変更を経ており、まだ多くの実装が最新ではありません。ブラウザは、すべてのブラウザがプロトコルをサポートしているわけではないため、別の落とし穴です(ChromeとFirefox、およびまもなくIE10がサポートします)。それはまだブラウザのかなりまともな部分であり、あなたがあなたにとって十分に良い場合は、最新バージョンでいくつかのセキュリティトリックも備えた目立たない接続の利点を享受できます。私はかなり素晴らしい.NETやNode.jsの実装を見てきましたが、きっとあなたはあなたの言語に適したものを見つけるでしょう。.NET Framework 4.5 には、' System.Net.WebSockets' という新しい光沢のある名前空間もあります。オープンソースの実装を選択しました。フレームワークがない場合でもフレームワークの更新は不要で、最新バージョン(最新のChromeまたはFirefoxブラウザにあるもの)と古いバージョンをサポートしているためです。これは SuperWebSocket と呼ばれ、CodePlex: http://superwebsocket.codeplex.com/で見つけることができます。私の場合のサーバー側は、私のデモをサポートするためにここにいるだけなので、実装は完璧ではなく、実際に大規模に使用できるわけでもないことに注意してください。サーバーは拡張可能なフレームワーク上に構築されているため、私のデモにはいくつかの参照があります。

この WebSocket サーバー実装で必要なアセンブリ。

以下は、設定セクションと初期化です。

<configuration>
  <configSections>
    <section name="socketServer" type="SuperSocket.SocketEngine.Configuration.SocketServiceConfig, SuperSocket.SocketEngine"/>
  </configSections>
 
  <socketServer>
    <servers>
      <server name="SuperWebSocket"
              serviceName="SuperWebSocket"
              ip="Any" port="2011" mode="Sync">
      </server>
    </servers>
    <services>
      <service name="SuperWebSocket"
               type="SuperWebSocket.WebSocketServer, SuperWebSocket" />
    </services>
  </socketServer>
<!--The rest is omitted-->

もちろん、これはアプリケーションの起動時に一度だけ呼び出されます (イベントまたはサーバー環境が提供する同等のもの)。.また、イベントをフックし、接続とデータ転送を非常に簡単に処理できます。セッションの確立時に、ビュー自体ではなく、セッションの確立時に初期データ (ローカルの Northwind データベースから生成された LINQtoSQL クラスから取得) をクライアントに送信する方法を次に示します。

private void StartWebSockServ()
{
    SocketServiceConfig config = ConfigurationManager.GetSection("socketServer") as SocketServiceConfig;
    // initialize with the above configuration
    if (!SocketServerManager.Initialize(config))
        return;
    // get an instance and set up:
    var socketServer = SocketServerManager.GetServerByName("SuperWebSocket") as WebSocketServer;
    HttpContext.Current.Application["WebSocketPort"] = socketServer.Config.Port;
    //set up the event handlers
    socketServer.NewSessionConnected += new SessionEventHandler<WebSocketSession>(Controllers.HomeController.socketServer_NewSessionConnected);
    socketServer.SessionClosed += new SessionEventHandler<WebSocketSession, SuperSocket.SocketBase.CloseReason>(Controllers.HomeController.socketServer_SessionClosed);
    socketServer.NewDataReceived += new SessionEventHandler<WebSocketSession, byte[]>(Controllers.HomeController.socketServer_NewDataReceived);
    socketServer.NewMessageReceived += new SessionEventHandler<WebSocketSession, string>(Controllers.HomeController.socketServer_NewMessageReceived);
 
    if (!SocketServerManager.Start())
        SocketServerManager.Stop();
    //start ticking data broadcast
    Controllers.HomeController.timer = new Timer(new TimerCallback(Controllers.HomeController.DataBroadcastOnChange));
    Controllers.HomeController.timer.Change(10000, 10000);
}

お気づきのように、私は実際にはライブデータを持っていません(そして、Twitter/Facebookのリーダーを作る気がしませんでした)ので、単純な時間指定イベントに常駐し、データにランダムな変更を生成しました。実際のアプリケーションでは、データレイヤー内のイベントを処理する必要があります。

public static void socketServer_NewSessionConnected(WebSocketSession session)
{
    NorthWindDataContext nw = new NorthWindDataContext();
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    session.SendResponse(serializer.Serialize(nw.Sales_by_Categories.Take(20)));
}

これは基本的にサーバーからデータをプッシュするために必要なすべてですが、それでも受信したメッセージを処理して双方向接続を利用することができます...たとえば、1 つのクライアントで行った変更を保存し、他のすべてのクライアントに送信します。

クライアント

WebSocket通信を処理するクライアント側の実装は非常に単純で、サーバー上のイベントのロジック(接続/データ受信/切断)にいくらか従っています。

public static void DataBroadcastOnChange(object state)
{
    //pretend we got changes from some service while really generating randoms:
    List<Sales_by_Category> changes = GetDataChanges();
    //send it back to the clients
    WebSocketServer socketServer = SocketServerManager.GetServerByName("SuperWebSocket") as WebSocketServer;
    var sessions = socketServer.GetAllSessions();
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    //broadcast the changes to all clients (if any)
    foreach (WebSocketSession session in sessions)
    {
        session.SendResponse(serializer.Serialize(changes));
    }
}

ポートはサーバー設定と同じであり、受信するデータは「onmessage」イベントのイベントパラメータに含まれることに注意してください。データを処理する前に、データをどのように処理するかを考えるポイントであるため、いくつかの準備が必要です。目標は、生きていると感じられるクライアントを持つアプリケーションであり、基本的なデータフィード配管をすでに提供していますが、初期データを表示するだけでは実際にはうまくいきませんか?確かに、強力なjQuery Gridを使用してそれを行うことができ、おそらく初期データを捨てて新しいデータをすぐに表示することを躊躇しないでしょうが、それは非常に優雅な解決策ではなく、上記はすでに、変更されたレコード全体ではなく、再度送信したいというヒントです。そのため、12.1 に付属する Infragistics jQuery ツールセットへのいくつかの優れた追加機能を利用できます - これは、データ ソースとグリッド ウィジェットの拡張機能であり、次のサポートを提供します。

Knockout!

Knockout.jsは、MVVMデザインパターンをサポートし、動的UIの作成を簡素化するJavaScriptライブラリであり、デモプロジェクトに最適です。それが何であるか、そしてそれが何ができるかを見たい場合は、たくさんのライブ例とチュートリアルでhttp://knockoutjs.com/をチェックしてください。何よりもまず、サイトから取得できるライブラリ(マッピングも取得することを確認してください)が必要になりますが、これはサイトから取得するか、NuGetもダウンロードできます。次に、スクリプトへのリンクをjQuery(およびUI)とともにコードと独自のローダーウィジェットに追加します。次に、モデルを作成します - すでに述べたように、Northwindからのデータを使用しており、それは「Sales by Category」ビューであり、これが含まれているデータ形式です。

$(document).ready(function () {
    //web socket handling
    if ('WebSocket' in window) {
        connect('ws://localhost:2011/');
    }
    else {
        alert("WebSockets don't seem to be supported on this browser.");
    }
 
    function connect(host) {
        ws = new WebSocket(host);
        ws.onopen = function () {
            notify('Connected!');
        };
 
        ws.onmessage = function (evt) {
            // handle data in evt.data
        };
 
        ws.onclose = function () {
            notify('Socket connection was closed!!!');
        };
    };
});

次に、次のように、これらの項目の観測可能なコレクションを使用してビュー モデルを作成します。

function Item(categoryID, categoryName, productName, productSales) {
//the unique key here is the prodcut name!
    return {
        categoryID: ko.observable(categoryID),
        categoryName: ko.observable(categoryName),
        productName: ko.observable(productName),
        productSales: ko.observable(productSales)
    };
};

「jsonData」は、サーバーからデータを取得するときに入力されることに注意してください(これについては以下で詳しく説明します)。ここまでで、データとモデルがあり、追加してUIの作成を開始できます

グリッド

ローダーウィジェットを設定することから始め、今回はグリッドを除いて、knockout.jsサポートの2つの拡張ファイルへのパスを追加します。

function ItemsViewModel() {
    var self = this;
 
    self.data = ko.observableArray([]);
 
    for (var i = 0; i < jsonData.length; i++) {
        self.data.push(new Item(jsonData[i].CategoryID, jsonData[i].CategoryName, jsonData[i].ProductName, jsonData[i].ProductSales));
    }
}

ノックアウトサポートには、更新機能が*必要です*ことに注意してください。これで、Knockout の "data-bind" 属性を使用してグリッドを定義できます。

$.ig.loader({
    scriptPath: "../../Scripts/js/",
    cssPath: "../../Content/css/",
    resources: "igGrid.Updating,extensions/infragistics.datasource.knockoutjs.js,extensions/infragistics.ui.grid.knockout-extensions.js"
});

ご覧のとおり、グリッド定義スタイルは括弧内で同じままであり、グリッド定義が作成された後は、通常使用するすべてのAPIメソッドを使用できます。

すべてを結びつける

もちろん、あとはデータをモデルにフィードし、残りは Knockout と Grid に任せる必要があります。ただし、明確にしておくべき小さなことがいくつかあります。まず、前述のようにランダムな変更を生成しており、変更されたアイテムだけをまったく同じ接続で送信したいので、互いに区別する方法が必要だったので、更新をマークするために空の最初のレコードを追加するだけです(データベースから来られないnullの主キーを確認します)。しかし、それは私の迅速で汚い解決策であり、あなたはあなた自身のものを持つか、更新のためだけにWebSocket接続を使用することができます。以下のスニペットでは、主キーを提供することでコレクションからアイテムIDを取得するために使用されるKnockoutユーティリティツールセットの一部と、更新されたセルを取得し、値が変更されたことをユーザーに明確にするために少し点滅させるために使用されるGrid APIの一部が動作していることがわかります。WebSocket の 'onmessgae' イベントは次のようになります。

<table id="grid"
       data-bind="igGrid: {
                    dataSource: data, width: 650, primaryKey: 'productName', autoCommit: true,
                    features: [ {
                        name: 'Updating', editMode: 'row',
                        },
                        {
                        name: 'Paging', pageSize: 10,
                        }
                    ],
                    enableHoverStyles: false,
                    autoGenerateColumns: false,
                    columns: [
                        {key: 'categoryID', headerText: 'Category ID', width: 100, dataType: 'number'},
                        {key: 'categoryName', headerText: 'Category Name', width: 200, dataType: 'string'},
                        {key: 'productName', headerText: 'Product Name', width: 130, dataType: 'string'},
                        {key: 'productSales', headerText: 'Sales', width: 170, dataType: 'number'}
                    ]}"></table>

カスタム通知機能やいくつかのcssスタイルなどのいくつかの小さな調整を除けば、これがアプリでできているものでした。また、接続(イニシャルの後)は更新に使用されるため、接続が切断されてもそれほど大きな問題にはなりません - 定義したイベント処理に基づいて、結果は次のようになります。

WebSocket 接続が失われたときの通知。

 

しかし、その後もグリッドは機能し続けるため、このアプリを丸みを帯びた機能にするために再接続を試みる方法を提供できます。

まとめ

私たちは、WebSocket接続、観測可能なKnockoutJSモデルによって処理されるライブデータフィードを使用して、モーションで満たされたアプリケーションを作成する方法を見てきました、私たちのjQueryグリッドは、自然な方法で、そしてユーザーのための楽しい経験で、クライアントサイドを常に実際のデータで最新の状態に保つために、動的に変更を動的に反映します!

スクリーンショットだけでは不十分なので、私は小さなビデオがあり、うまくいけばすぐに来て、2012年の最初のリリース後のデモプロジェクトは事実です。乞うご期待!

デモを予約