How to build a live datagrid with Nitrogen

One of the most useful element in Nitrogen Elements project is jqGrid control.
jqGrid is a Nitrogen wrapper for a popular javasript library which allows to represent and manipulat tabular data on the web. There are lots of examples for this control which are available here.

The nice thing about most of the data grid controls (jqGrid is one of many available) is that they usually works with XML or JSON data sources. So, you can initialize the datagrid on the client side and populate it with the data which are retrieved from the URL which returns JSON or XML string.

Here is the example to add #jqgrid{} to your page:

	#jqgrid{
	    id = jqgrid,
	    options=[
		{url, 'get_jqgrid_data'},
		{datatype, <<"json">>},
		{colNames, ['ID', 'Name', 'Values']},
		{colModel, [
		    [{name, 'id'}, {index, 'id'}, {width, 55}],
		    [{name, 'name'}, {index, 'name1'}, {width, 80}],
		    [{name, 'values1'}, {index, 'values1'}, {width, 100}]
		]},
		{rowNum, 10},
		{rowList, [10, 20, 30]},
		{sortname, 'id'},
		{viewrecords, true},
		{sortorder, <<"desc">>},
		{caption, <<"JSON Example">>},
		{groupColumnShow, false},
		{loadonce, false},
		{scrollOffset, 0}, %% switch off scrollbar
		{autowidth, true} %% fill parent container on load
	    ]

You will need to specify the data type and url from which the control gets the data:

{url, 'get_jqgrid_data'},
{datatype, <<"json">>},

You also need to specify the data model: column names for the table view:

	{colNames, ['ID', 'Name', 'Values']},
		{colModel, [
		    [{name, 'id'}, {index, 'id'}, {width, 55}],
		    [{name, 'name'}, {index, 'name1'}, {width, 80}],
		    [{name, 'values1'}, {index, 'values1'}, {width, 100}]
		]},

The datasource URL point to the REST handler which returns JSON string:

-module(get_jqgrid_data).

-export([init/3, content_types_provided/2, to_json/2]).

init(_Transport, _Req, []) ->
    {upgrade, protocol, cowboy_rest}.

content_types_provided(Req, State) ->
    {[
      {<<"application/json">>, to_json}
     ], Req, State}.

to_json(Req, State) ->
    Data = {struct, [{<<"total">>, 1},
		     {<<"page">>, 1},
		     {<<"records">>, 2},
		     {<<"rows">>, [{struct, [{<<"id">>, 1}, {<<"cell">>, [<<"1">>, <<"cell11">>, <<"values11">>]}]},
				   {struct, [{<<"id">>, 2}, {<<"cell">>, [<<"2">>, <<"cell15">>, <<"values22">>]}]}
				  ]}
		    ]},
    Data1 = iolist_to_binary(mochijson2:encode(Data)),
    {Data1, Req, State}.

Here is how the #jqgrid{} defined with the above code will look at your page:
Screen Shot 2013-03-24 at 22.26.15

With a bit of extra code you can turn this datagrid live and make it auto-update from the continuous data stream by hooking it up via Websocket to the module which will push JSON updates from the server side.

It works very similar to the viz.js example.

We add a button and on_click event open a Websocket connection to “ws://localhost:8000/jqdata”, Websocket connection is served by Jqdata.erl module which will generate and push json updates to the browser:

-module(jqdata).
-behaviour(cowboy_websocket_handler).

-export([init/3]).
-export([websocket_init/3]).
-export([websocket_handle/3]).
-export([websocket_info/3]).
-export([websocket_terminate/3]).

init({tcp, http}, _Req, _Opts) ->
    {upgrade, protocol, cowboy_websocket}.

websocket_init(_TransportName, Req, _Opts) ->
    erlang:start_timer(1000, self(), get_data()),
    {ok, Req, undefined_state}.

websocket_handle({text, Msg}, Req, State) ->
    {reply, {text, << "", Msg/binary >>}, Req, State};
websocket_handle(_Data, Req, State) ->
    {ok, Req, State}.

websocket_info({timeout, _Ref, Msg}, Req, State) ->
    erlang:start_timer(1000, self(), get_data()),
    {reply, {text, Msg}, Req, State};
websocket_info(_Info, Req, State) ->
    {ok, Req, State}.

websocket_terminate(_Reason, _Req, _State) ->
    ok.

get_data() ->
    Data = {struct, [{<<"total">>, 1},
		     {<<"page">>, 1},
		     {<<"records">>, 4},
		     {<<"rows">>, [{struct, [{<<"id">>, 1}, {<<"cell">>, [<<"1">>, <<"cell11">>, random:uniform(100)]}]},
				   {struct, [{<<"id">>, 2}, {<<"cell">>, [<<"2">>, <<"cell15">>, random:uniform(50)]}]},
				   {struct, [{<<"id">>, 3}, {<<"cell">>, [<<"3">>, <<"cell55">>, random:uniform(20)]}]},
				   {struct, [{<<"id">>, 4}, {<<"cell">>, [<<"4">>, <<"cell66">>, random:uniform(10)]}]}
				  ]}
		    ]},
    iolist_to_binary(mochijson2:encode(Data)).

We also add a callback to the “on_message” event, with it we set jqgrid datatype to “jsonstring” and datastr to the json string sent from the server. We then trigger jqgrid update event:

event({Tag, connect}) ->
    Server = wf:q(server_txt),
    %?PRINT({gqgrid_server, Server}),
    wf:wire(#ws_open{server = Server, func = "function(event){console.log('open')};"}),
    wf:wire(#ws_message{func = wf:f("function(event){$(obj('~s')).jqGrid('setGridParam',
	                 {datatype:\"jsonstring\", datastr:event.data}).trigger(\"reloadGrid\")};", [jqgrid])}),
    wf:wire(#ws_error{func = "function(event){console.log('error')};"}),
    wf:replace(conn, #button{id = conn, text = "Disconnect", actions = [#event{type = click, postback = {Tag, disconnect}}]});

With all of this we will have the following ticking datagrid, if you run the example you will see the changing values in “Values” column:

Screen Shot 2013-03-24 at 23.25.53

The full example is here.

Advertisement
This entry was posted in Cowboy, custom Nitrogen elements, Erlang, Nitrogen, Nitrogen_Elements, Uncategorized and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s