Wednesday, June 10, 2009

Dynamic Data in Exhibit Views with Spring and jQuery

Note: See the addendum at the bottom for how to use this with Exhibit 3.

The SIMILE Widget, Exhibit is a great tool for displaying data of various sorts with a number of different visualization types. It provides a very intuitive view of the data by utilizing dynamic filtering of data facets. Most of the examples online utilize static JSON files as the data sources. I wanted to use Exhibit to provide filtering capabilities on the results of a standard database query.

I had asked a friend (@ericneumann) if it was possible to do this and he said something about dynamic RDF views and remote calls. So I set off on a quest to try and figure this out. I remember going threw a number of failed attempts before coming to the final working solution. There were some, in my opinion, non intuitive tweaks and settings that had to be done. I will try and outline the process here.

  1. Create a web service that produces JSON data based on a query.

  2. Using the Spring Framework I used a SimpleFormController which returned an abstract view of content type "application/json; charset=utf-8".

    After you get the writer from the response ( final PrintWriter writer = response.getWriter(); ), build a JSON object with json-lib, and then write the object to the writer.
    jsonObj.write(writer); writer.close();

    You can see all the code here. Of course you have to set up the context and servlet configurations for the service as well. Don't worry I will attach a demo at the end of the post so you can see all the application context fun.

  3. Create the web page with the Exhibit configuration.

  4. This tutorial is a good place to start and a new one was just posted today. One key difference in a dynamic Exhibit is the omission of the exhibit data link tag
    <link href="genes.json" type="application/json" 
    
    rel="exhibit/data"/>

    Otherwise set up your filters and views as you would in any Exhibit view. Here is the complete html markup.

  5. Set up the inline importer

  6. This part uses the Exhibit api as well as jQuery. This was the hardest part for me to figure out so feel lucky you didn't have to. I put the following in the html header rather then in a javascript link. I doubt that is necessary but it seemed that after hours of tweaking this is how it worked. So you might play around to see what works best for you.

    <script type="text/javascript">
           Exhibit.InlineImporter = { };
           Exhibit.importers["inline"] 
                      = Exhibit.InlineImporter;
           Exhibit.InlineImporter.load 
                      = function(data, database, cont) {
    
               Exhibit.UI.showBusyIndicator();
               database.loadData(data);
               Exhibit.UI.hideBusyIndicator();
               if (cont) cont();
           };
    
           $(document).ready(function(){
               $("#probes").focus();
               $('#geneSearchResults').hide();
               $('#geneSearchFilters').hide();
           });
       </script>


  7. Set up ajax call and inject data into Exhibit view

  8. This was a bit easier to do. You simply set up the ajax call with jQuery as usual and then when you get the JSON data back you put it in the Exhibit importer.
        Exhibit.UI.showBusyIndicator();
        $.getJSON(url,
        {mode: 'text', obj: 'probes', probes: probes},
           function(data, status){
              $('#geneSearchResults').hide();
              $('#geneSearchFilters').hide();
              // clear the whole database
              window.database.removeAllStatements(); 
              if(data.items.length>0){
                 Exhibit.InlineImporter
                      .load(data, window.database, "");
              }else{
                 alert("No data")
              }
              Exhibit.UI.hideBusyIndicator();
              $('#geneSearchFilters').show();
              $('#geneSearchResults').show();
                   
           });

I have a complete spring web app (Example Exhibit Spring app) that shows an implementation of of this method. You need to install tomcat and ant. Read the README file for more info.

Addendum: How to do this with Exhibit 3.

Exhibit 3 (http://www.simile-widgets.org/exhibit3/) makes this a lot easier. All of the item processing, database building and UI buys indicator handling is already handled by the importers. I did not have to set up an Inline importer and Basically all the $.getJSON code can be replaced with

Exhibit.Importer.getImporter( "application/json").load(url, window.database, callBack());

My callBack() is optional.

You should reset your filters before you load in new data. I did this as follows.

//Code adapted from
// https://github.com/simile-widgets/exhibit/blob/v3.1.0rc/scripted/src/scripts/ui/widgets/collection-summary-widget.js
function clearDataBaseAndFilters(){
    // clear the whole database
    window.database.removeAllStatements(); 

    var state, collection;
    collection = window.exhibit.getUIContext().getCollection();

    Exhibit.jQuery(collection.getElement()).trigger("onResetAllFilters.exhibit");
    state = collection.clearAllRestrictions();

    Exhibit.History.pushState(
        state.data,
        Exhibit._("%widget.collectionSummary.resetActionTitle")
    );
}



1 comment:

  1. Thanks very much for the post. It was very useful.

    Thanks,
    Mahesh Kulkarni
    BG Medicine.

    ReplyDelete