UI with ReactJS and ImmutableJS: Difference between revisions

From Genecats
Jump to navigationJump to search
(initial outline)
 
Line 61: Line 61:


== The CGI speaks JSON now ==
== The CGI speaks JSON now ==
One nice feature of Javascript is its object literal notation because it is able to express nested data structures reasonably compactly and, with pretty-printing, in a fairly human-readable form.  Javascript data structures without loops or function references can easily be serialized into [http://www.json.org/ JSON], a more restricted notation that is easy to parse.  That makes JSON an ideal interchange format for data that's not too big when working with Javascript and some other language, in our case C. 
The UI model sends JSON-encoded requests to the server/CGI, and expects JSON responses.  For example, if the user selects a different clade, the model formulates this command as a Javascript object:
{ changeClade: { newValue: "rodent" } }
js/model/lib/cart.js makes a request to the CGI with the param <code>cjCmd</code> set to the CGI-encoded JSON string representing that object.  Now the CGI needs to
# recognize the <code>cjCmd</code> param -- that means output will be JSON, not HTML or text
# decode and parse <code>cjCmd</code>'s JSON value into a C data structure
# act on the command(s) as specified in the data structure and write out any reponse data as JSON.
The new lib module hg/inc/cartJson.h provides a function <code>cartJsonExecute</code> that does part 2 (using src/inc/jsonParse.h) and also part 3 for common commands like changing clade/genome/database or retrieving track & table menu data (using hdb, cart, trackDb etc and src/inc/jsonWrite.h).  (For a list of those common commands, see the source code for <code>cartJsonNew</code>.)  It also pushes & pops a warning handler that accumulates warning messages into a string that will be added to the response JSON, e.g. <code>warning: "ftp server timed out etc..."</code>, that the UI can then present to the user or not.  CGIs create a cartJson object, and can plug in their own part 3's by writing handler functions with this signature:
typedef void CartJsonHandler(struct cartJson *cj, struct hash *paramHash);
/* Implementation of some command; paramHash associates parameter names with
  * jsonElement values. */
and then registering them with <code>cartJsonRegisterHandler</code>. 
Here is an example use of cartJson by a CGI that needs one app-specific command, getThingie.  First the CGI defines a handler function:
static void getThingie(struct cartJson *cj, struct hash *paramHash)
/* Write JSON for an object with some info */
{
char *foo = cartJsonOptionalParam(paramHash, "foo");
// ... compute ... use cj->cart if we need the cart ...
jsonWriteObjectStart(cj->jw, "thingie");
jsonWriteString(cj->jw, "title", fooDerivative);
jsonWriteString(cj->jw, "url", etc);
jsonWriteObjectEnd(cj->jw);
}
Then when the CGI detects the special parameter <code>cjCmd</code>, it creates a cartJson object, registers the handler function, and calls <code>cartJsonExecute</code> to do the rest (including printing out the Content-Type header).
static void doCartJson()
/* Perform UI commands to update the cart and/or retrieve cart vars & metadata. */
{
struct cartJson *cj = cartJsonNew(cart);
cartJsonRegisterHandler(cj, "getThingie", getThingie);
cartJsonExecute(cj);
}
void doMiddle(struct cart *theCart)
/* Depending on invocation, either respond to an ajax request or display the main page. */
{
cart = theCart;
if (cgiOptionalString(CARTJSON_COMMAND))
    doCartJson();
else
    doMainPage();
}

Revision as of 00:01, 13 January 2015

Background

Evolution of Javascript in kent/src

Before JS: just the CGI

JS inlined in HTML

AJAX and JQuery: manipulating the DOM

MV*

An abandoned experiment

ReactJS & JSX

Simplicity of top-down render, made efficient

JSX: HTML-ish with the power of JS

Lifecycle methods of React components

React mixins

The Flux architecture: why not?

ImmutableJS

The new architecture

Bootstrapping: the CGI prints minimal HTML

ImModel: monolithic UI state model using ImmutableJS

Handling user actions

Handling AJAX responses

Undo and redo

ImModel mixins

View: React components using ImmutableJS

Avoid internal state when possible

Using UI state from ImModel

Passing user actions to ImModel

JQueryUi: oil & water with React, but still too useful to drop

The CGI speaks JSON now

One nice feature of Javascript is its object literal notation because it is able to express nested data structures reasonably compactly and, with pretty-printing, in a fairly human-readable form. Javascript data structures without loops or function references can easily be serialized into JSON, a more restricted notation that is easy to parse. That makes JSON an ideal interchange format for data that's not too big when working with Javascript and some other language, in our case C.

The UI model sends JSON-encoded requests to the server/CGI, and expects JSON responses. For example, if the user selects a different clade, the model formulates this command as a Javascript object:

{ changeClade: { newValue: "rodent" } }

js/model/lib/cart.js makes a request to the CGI with the param cjCmd set to the CGI-encoded JSON string representing that object. Now the CGI needs to

  1. recognize the cjCmd param -- that means output will be JSON, not HTML or text
  2. decode and parse cjCmd's JSON value into a C data structure
  3. act on the command(s) as specified in the data structure and write out any reponse data as JSON.

The new lib module hg/inc/cartJson.h provides a function cartJsonExecute that does part 2 (using src/inc/jsonParse.h) and also part 3 for common commands like changing clade/genome/database or retrieving track & table menu data (using hdb, cart, trackDb etc and src/inc/jsonWrite.h). (For a list of those common commands, see the source code for cartJsonNew.) It also pushes & pops a warning handler that accumulates warning messages into a string that will be added to the response JSON, e.g. warning: "ftp server timed out etc...", that the UI can then present to the user or not. CGIs create a cartJson object, and can plug in their own part 3's by writing handler functions with this signature:

typedef void CartJsonHandler(struct cartJson *cj, struct hash *paramHash);
/* Implementation of some command; paramHash associates parameter names with
 * jsonElement values. */

and then registering them with cartJsonRegisterHandler.

Here is an example use of cartJson by a CGI that needs one app-specific command, getThingie. First the CGI defines a handler function:

static void getThingie(struct cartJson *cj, struct hash *paramHash)
/* Write JSON for an object with some info */
{
char *foo = cartJsonOptionalParam(paramHash, "foo");
// ... compute ... use cj->cart if we need the cart ...
jsonWriteObjectStart(cj->jw, "thingie");
jsonWriteString(cj->jw, "title", fooDerivative);
jsonWriteString(cj->jw, "url", etc);
jsonWriteObjectEnd(cj->jw);
}

Then when the CGI detects the special parameter cjCmd, it creates a cartJson object, registers the handler function, and calls cartJsonExecute to do the rest (including printing out the Content-Type header).

static void doCartJson()
/* Perform UI commands to update the cart and/or retrieve cart vars & metadata. */
{
struct cartJson *cj = cartJsonNew(cart);
cartJsonRegisterHandler(cj, "getThingie", getThingie);
cartJsonExecute(cj);
}

void doMiddle(struct cart *theCart)
/* Depending on invocation, either respond to an ajax request or display the main page. */
{
cart = theCart;
if (cgiOptionalString(CARTJSON_COMMAND))
    doCartJson();
else
    doMainPage();
}