The Source for Java Technology Collaboration
User: Password:



Carla Mott's Blog

September 2007 Archives


jMaki app using Google Gears

Posted by carlavmott on September 28, 2007 at 04:50 PM | Permalink | Comments (0)

I created a simple app as a demo that uses Google Gears for local storage and also sends data to the server upon user request. I showed this at AjaxWorld where there was alot of interest so I decided I would blog about it.

Building on the Get Widget Data Sample that is part of the jMaki bundle, I added Google Gears so that it will be called when the save icon of the editor is clicked. The data in the editor is saved locally each time the editor save button is clicked. I kept the functionality of sending the data back to the server by simply adding a 'submit' button which does a jmaki.doAjax call sending the contents of the editor to a server side component.

If you are using NetBeans you can install the Google Gears Extension comp lib and add the appropriate code by dragging from the palette. To install a comp lib go to tools -> palette -> add jMaki library. If you are not using NetBeans then follow the instructions on Greg's blog on adding google gears extension to you app.

In order to save the editor contents to the local storage add the following code (in addition to the extension handler) to the onSave handler in the glue.js file.

// map topics ending with  /onSave to the handler

jmaki.subscribe("*onSave", function(args) {
    // empty the table then save it
    jmaki.publish("/google/gears/execute",
    { query : 'delete from jmaki',
     callback : function() {
       jmaki.log("Removing Previous Values");
       jmaki.publish("/google/gears/execute",
          { query : 'insert into jmaki values (?, ?)',
             args : [args.value, new Date()],
         callback : function() {
             jmaki.log("Saved " + args.value);
         }
        });
       }
    });
});

This handler gets called each time an onSave event occurs and saves data locally. A real app may use a specific topic for the editor in case other widgets in the application fire an onSave event. In this example I clear the local store each time I save.

I add a button to the the page which will push the contents of the editor to a server side components which can then save the data in a data base there. I've included the code snippet for the button and the editor widgets here:

 <div id="main">
                <div id="leftSidebar">
                    <a:widget name="yahoo.button" value="{label : 'Submit contents', action : {topic : '/submitData'}}"/>
                  
                    
                </div> <!-- leftSidebar -->

                <div id="content" style="height:400px">
                    
                    <a:widget name="dojo.editor" id="myeditor" value="@{window.editorData}" />

                </div> <!-- content -->       
            </div> <!-- main -->


In PHP the code looks like:

 <div id="main">
                <div id="leftSidebar">
                    <?php
  addWidget( array ( "name" =>"yahoo.button" value=>"{label : 'Submit contents', action : {topic : '/submitData'}}" )); 
?>
                  
                    
                </div> <!-- leftSidebar -->

                <div id="content" style="height:400px">
                    
                    <?php
  addWidget( array ( "name" =>"dojo.editor" id=> "myeditor" value=>"Edit this value." )); 
?>

                </div> <!-- content -->       
            </div> <!-- main -->


Notice that I use the id attribute on the editor widget tag. I did this because it is easy to get the widget later in the handler. I also set the topic in the button widget tag so I can easily set the handler.

I now need to add a handler for the button which sends data back to the server. The complete code for glue.js is listed below:

jmaki.namespace("jmaki.listeners");

jmaki.subscribe("/submitData", "jmaki.listeners.editorListener");

jmaki.listeners.editorListener = function(args) {
    var editorId = jmaki.getWidget('myeditor');
    var contentValue = editorId.getValue();
    if ( typeof contentValue != 'undefined' ) {
        jmaki.log("Editor content=  " + contentValue);
        // send data back to server
         jmaki.doAjax({method: "POST",
                               url: "Service.jsp",
                               content: {message: contentValue },
                               callback: function(_req) {
                                   // handle any errors
                               }
                           });
    } 
};

// map topics ending with  /onSave to the handler

jmaki.subscribe("*onSave", function(args) {
    // empty the table then save it
    jmaki.publish("/google/gears/execute",
    { query : 'delete from jmaki',
     callback : function() {
       jmaki.log("Removing Previous Values");
       jmaki.publish("/google/gears/execute",
          { query : 'insert into jmaki values (?, ?)',
             args : [args.value, new Date()],
         callback : function() {
             jmaki.log("Saved " + args.value);
         }
        });
       }
    });
});

jmaki.subscribe("/jmaki/runtime/extensionsLoaded", function() {

  jmaki.publish("/google/gears/execute", 
    { query : 'select * from jmaki order by Timestamp desc',
     callback : function(_rs) {
         if (typeof _rs != "undefined" &&
             typeof _rs.isValidRow != "undefined" &&
             _rs.isValidRow()) {
             // set data on the global window object for later access
             if (typeof _rs.field != "undefined") window.editorData = _rs.field(0);
             else window.editorData = "";
             jmaki.log("Loaded Editor Data: " + editorData);
         } else {
             window.editorData = "";
             jmaki.log("No data available");
         }
         jmaki.log('done');
     }
    });
    
});

The code above shows how I used the settings in the tag to access the editor widget and also send data to the server. When the 'Submit contents' button is clicked the function jmaki.listeners.editorListener is called. That function looks up the editor widget using the id that we set on the widget tag. It then calls a function in the wrapper that returns the contents of the editor. The jmaki.doAjax call send the data back to a server component called Service.jsp.

That's it. I have created a war file of the demo app and posted here so you can see working app.

How to implement data pagination in jMaki tables

Posted by carlavmott on September 19, 2007 at 05:31 PM | Permalink | Comments (6)

The question about paging through data in a table comes up enough that I decided to answer in a blog. Using some code Greg posted in an email a few months back let's create a simple example of paging through data in a table. In this example, we will use the Dojo fisheye widget to drive the pages that are displayed in a Dojo table. Each time the user clicks on an icon the table displays different parts of the data depending on the icon clicked. Most of the work for determining what to display is done in JavaScript code. The data is in a JSP file but can be in some serverside component that returns the data in JSON format. We will use the jmaki.doAjax call to get the data when the page loads.

I created the index page with the standard jMaki layout and added the Dojo fisheye widget in the left side bar and the Dojo table widget in the main area. In this case we will not provide any data for the rows but rely on the paging mechanism to supply all data. index.jsp looks like:

            <div id="main">
                <div id="leftSidebar">
                    
                <a:widget name="dojo.fisheye" args="{orientation:'vertical'}" value="[ 
     {iconSrc:'https://ajax.dev.java.net/images/blog_murray.jpg',
      label : 'Page 1',
      action : { message : {value : 'books1'}}
     },
     {iconSrc:'https://ajax.dev.java.net/images/chinnici.jpg',
      label : 'Page 2',
      action : { message : {value : 'books2'}}
     },
      {iconSrc:'https://ajax.dev.java.net/images/JayashriVisvanathan.jpg',
      label : 'Page 3',
      action : { message : {value : 'books3'}}
     }         ]"/>

                    
                </div> <!-- leftSidebar -->

                <div id="content" style="height:400px">
                    
                 
                    <a:widget name="dojo.table"
                    subscribe="/datatable"
                    publish="/datatable"
                    value="{columns : [
     { label : 'Title', id : 'title'},
     { label :'Author', id : 'author'},
     { label : 'ISBN', id : 'isbn'},
     { label : 'Description', id : 'description'}
     ],
     rows : [
     ]
     }" />

     
            </div> <!-- content -->
        
            </div> <!-- main -->

Some things to note. The fisheye widget publishes to the topic '/dojo/fisheye' unless it is over written. In this case we use the default. Remember that the user will click on an icon in the fisheye and that will result in loading different pages of data. The way that I communicate the page to load is by using an action. In this case I use a strings 'book1', 'book2' and 'book3' to identify the page. The strings are passed using the property 'value'. Finally, notice I reset both subscribe and publish attributes for the table widget. More on this later.

Next I created a JSP called books.jsp which contains all the data we're going to use. The data is already in JSON format. books.jsp looks like:

[
 [
  { id : 'i1', title : 'Book Title 1', author : 'Author 1', description : 'A look at the samples ', url : 'https://ajax.dev.java.net/samples'},
  { id : 'i2', title : 'Book Title 2', author : 'Author 2', description : 'A Some long description', url : 'https://ajax.dev.java.net/samples'},
  { id : 'i3', title : 'Book Title 3', author : 'Author 3', description : 'A Some long description', url : 'https://ajax.dev.java.net/samples'},
  { id : 'i4', title : 'Book Title 4', author : 'Author 4', description : 'A Some long description', url : 'https://ajax.dev.java.net/samples'},
  { id : 'i5', title : 'Book Title 5', author : 'Author 5', description : 'A Some long description', url : 'https://ajax.dev.java.net/samples'}
 ],
 [
  { id : 'i6', title : 'Book title 6', author : 'Author 2', description : 'A Some long description', url : 'https://ajax.dev.java.net'},
  { id : 'i7', title : 'Book title 7', author : 'Author 2',  description : 'A Some long description', url : 'https://ajax.dev.java.net'},
  { id : 'i8', title : 'Book title 8', author : 'Author 2', description : 'A Some long description', url : 'https://ajax.dev.java.net'},
  { id : 'i9', title : 'Book title 9', author : 'Author 2',  description : 'A Some long description', url : 'https://ajax.dev.java.net'},
  { id : 'i10', title : 'Book Title 10', author : 'Author 2',  description : 'A Some long description', url : 'https://ajax.dev.java.net'}
 ],
 [
  { id : 'i11', title : 'Book Title 11', author : 'Author 3', description : 'A Some long description', url : 'https://ajax.dev.java.net/about'},
  { id : 'i12', title : 'Book Title 12', author : 'Author 3', description : 'A Some long description', url : 'https://ajax.dev.java.net/about'},
  { id : 'i13', title : 'Book Title 13', author : 'Author 3', description : 'A Some long description', url : 'https://ajax.dev.java.net/about'},
  { id : 'i14', title : 'Book Title 14', author : 'Author 3', description : 'A Some long description', url : 'https://ajax.dev.java.net/about'},
  { id : 'i15', title : 'Book Title 15', author : 'Author 3', description : 'A Some long description', url : 'https://ajax.dev.java.net/about'}
 ]
]

The data is formated as an array of arrays of objects. Each array of objects is a page of data. Most of the ids in the data should look familiar as they map to the columns. The last element is a url. It is not represented on the page but can be used. As you will see we will write the code so when the user clicks on the row we will navigate to that url. In order to get that url we need to write a handler for the onSelect event for a table. The handler will be listening to the '/datatable/ topic and that is why we added a publish argument to the table tag.

Finally I updated the glue.js file to add the JavaScript code for getting the data and providing the appropriate data for the table. The JavaScript code follows:

/*
 * These are some predefined glue listeners that you can
 *  modify to fit your application.
 *
 * This file should not placed in the /resources directory of your application
 * as that directory is for jmaki specific resources.
 */

// uncomment to turn on the logger
jmaki.debug = true;
// uncomment to show publish/subscribe messages
jmaki.debugGlue = true;


var books; // load using an ajax request

jmaki.doAjax({
  url : "books.jsp",
  callback : function(req) {
   books = eval(req.responseText);
   jmaki.log("Loaded Books " + books);
  }
});



// map to the defualt topic /dojo/fisheye to fisheye handler
jmaki.subscribe("/dojo/fisheye*", function(args) {
    switch (args.message.value) {
        case  'books1' : {
            // clear the rows
            jmaki.publish("/datatable/clear", {});
            jmaki.publish("/datatable/addRows", { value : books[0]});
            return;
        }
        case  'books2' : {
            // clear the rows
            jmaki.publish("/datatable/clear", {});
            jmaki.publish("/datatable/addRows", { value : books[1]});
            return;
        }
        case  'books3' : {
            // clear the rows
            jmaki.publish("/datatable/clear", {});
            jmaki.publish("/datatable/addRows", { value : books[2]});
            return;
        }

    }
 });

 // the handler for the datatable
jmaki.subscribe("/datatable/onSelect", function(args) {
/*    var row = getRow(args.targetId);
    if (row){
        jmaki.log("url=" + row.url);
        window.location.href = row.url;
    }
    */
    if(args.value && args.value.url){
        jmaki.log("url=" + args.value.url);
        window.location.href = args.value.url;
    }
});

// find the row
function getRow(targetId) {
  for (var i=0; i < books.length; i++) {
      for (var ii=0; ii < books[i].length; ii++) {
          if (books[i][ii].id == targetId) return books[i][ii];
      }
  }

}
The first thing that happens is I make a jamki.doAjax() call to get the data and store it locally. Then I define two handlers to manipulate the data. I subscribe to '/dojo/fisheye' and based on which string I get (book1, book2, book3) I publish the appropriate section of data to the '/datatable' topic which the Dojo table is subscribed. This way I control what is loaded into the table. Since I formated the data in way that matches the table data model paging becomes really easy.

I also have a handler for the onSelect event from the table. When the user clicks on a row the table widget will automatically publish the id of the row that was selected. There have two ways of getting the url. Since this is a table widget the data of the selected row is included in the payload so I can just look at the value of the url property. The second way to find the url is to get the targetId property which tells me the id of the thing that was selected (true for all onSelect events) and use that to traverse through the data and find the appropriate row. Then I can just access the url property. I included the code for both since both are useful.

Tha's it. You now have a way of paging through the data in the table widget in jMaki.

Simple end to end jMaki application

Posted by carlavmott on September 11, 2007 at 12:55 PM | Permalink | Comments (0)

This blog describes how to use jMaki actions and events to drive behavior or data in widgets. I will build a simple end to end application that shows the main concepts of widgets driving widgets (behavior) and also getting data from a server when needed.

I actually created two versions of the application, JSP and PHP, for this blog, Each have three Yahoo buttons named 'select CA', 'set Values' and 'reset Values' and one Dojo combobox. The buttons will drive the content of the combobox. In this application, one of the buttons will cause the application to access the server for data which will become the values in the combobox.

The main code of the JSP page looks like:

 <div class="main">
     <div class="leftSidebar">
                    
        <a:widget name="yahoo.button" value="{label : 'select CA', 
                     action : {topic : '/combobox/select', message : {targetId : 'bar'}}}"/>
        <a:widget name="yahoo.button" value="{label : 'setValues', 
                     action : {topic : '/mytopic/setvalues'}}" />
        <a:widget name="yahoo.button" value="{label : 'resetValues', 
                     action : {topic : '/mytopic/resetvalues'}}" />           
     </div> <!-- leftSidebar ->

     <div class="content" style="height:400px">
                    
        <a:widget name="dojo.combobox" subscribe="/combobox"
            value="[
                 {label : 'Alabama', value : 'AL'},
                 {id : 'bar',label : 'California', value : 'CA'},
                 {label : 'New York', value : 'NY', selected : true},
                 {label : 'Texas', value : 'TX'}	           
            ]" />

      </div> <!-- content -->
        
  </div> <!-- main -->

The PHP looks like:

<div class="main">
     <div class="leftSidebar">
                    
        < addWidget( array("name" => "yahoo.button", "value"=>"{label : 'select CA', 
                     action : {topic : '/combobox/select', message : {targetId : 'bar'}}}"/>
        < addWidget( array("name" => "yahoo.button",
                                     "value" => "{label : 'setValues', 
                     action : {topic : '/mytopic/setvalues'}}" />
        <addWidget( array("name" => "yahoo.button",
                                     "value" => "{label : 'resetValues', 
                     action : {topic : '/mytopic/resetvalues'}}" />           
     </div> <!-- leftSidebar ->

     <div class="content" style="height:400px">
                    
        < addWidget( array("name" => "dojo.combobox",
                                       "subscribe" => "/combobox",
                                       "value" => "[
                 {label : 'Alabama', value : 'AL'},
                 {id : 'bar',label : 'California', value : 'CA'},
                 {label : 'New York', value : 'NY', selected : true},
                 {label : 'Texas', value : 'TX'}	           
            ]" />

      </div> <!-- content -->
        
  </div> <!-- main -->

As an aside, I'd like to point out that the data model for the widgets is the same for the two platforms.

Notice that I used jMaki actions to drive the combobox. We can select a value to be displayed in the combobox by simply publishing an event to a topic along with the id of what we want selected. In jMaki, we use publish and subscribe extensively in widget to widget communication. See my article A practical guide to jMaki events for more information on the publish/subscribe mechanism in jMaki. Let's look at how to use actions more closely.

In the first button, we want to select one of the items, California, in the combobox. To get the desired result, we use the action property to publish to a topic. The combobox implements the jMaki combobox data model which means it implements the data model and two handlers, select and setValues. This means that there is code in the widget wrapper that is called when an event is published to /dojo/combobox/select and /dojo/combobox/setValues. You have the option to override the topic name if you like. In this example I have overwritten the default topic to be /combobox. Now to select the item 'California' all I need to do is send an event to the appropriate topic with the id of the item I want selected. As you can see in the combobox tag above I used the id 'bar' in the second item. I pass that same id as the targetId property in the action object. An event is published to the '/combobox/select' topic along with a payload containing a value object with the value to be selected. The underlying widget initialization code takes care of executing the appropriate code needed to select the item. That's right, you didn't have to write any JavaScript code at all. We believe that simple operations should be just that simple.

In the case of selecting which item will be displayed in the combobox we had all the data that we needed. Some times however, you may want to get data from a data base and so you will need to write a handler to do that. The second button causes the application to make a call back to the server to get data and replaces the values in the combobox with that new data. In that action object I simple have a topic property where I have specified a new topic name and that's all. This means that an event is published to that new topic. I then need to add a handler which will provide the appropriate code which is associated with that topic. To do that I add JavaScript code in the glue.js file which the framework automatically loads with each page. To associate my new handler to the topic that the action object is publishing to I use the jmaki.subscribe function. See the code below:

jmaki.subscribe("/mytopic/setvalues", function (args) {

   jmaki.doAjax({method: "POST",
               url: "data.jsp" ,
                callback: function(_req) {
                    var tmp = _req.responseText;
                    var obj = eval ("(" + tmp + ")"); 
                    jmaki.publish("/combobox/setValues",  obj );
                    // handle any errors
                }
   });

});

All I have to do is use the same string in both the topic property and the jmaki.subscribe function and the handler will get called.

Once in my handler, I use the jmaki.doAjax call to connect to the server code. This function is a generalized XMLHttpRequest call which can be used from evaluated code. jmaki.doAjax also allows you to set a timeout which will abort the call to the server if exceeded, it encodes any content before contacting the server and it provides some basic error handling.

In this application the server code simply returns the data in JSON format. For the purpose of this blog I created a JSP and PHP file that contained the data to be returned. Note that the filename in the jmaki.doAjax call must match the name of the file you created. I do have to convert the data returned to a JSON object because what is returned is a string so here I used the eval function and then publish that object as the payload to another topic. By publishing the data to the '/combobox/setValue' topic I have sent the data that I want loaded in the combobox to the widget because it is already subscribed to that topic.

The contents of data.jsp or data.php follows:

[ {  label: "Taipei, Taiwan ", value: "Taipei, Taiwan" , selected: true } , 
{  label: "Shanghai, China", value: "Shanghai, China" },
{ label: "Rome, Italy", value: "Rome, Italy"},
{ label: "Paris, France", value: "Paris, France"}]

For completeness I have include the handler code for the reset button. I used an action to call another handler but I didn't access the serve r to get the data as it included in the handler code itself.

jmaki.subscribe("/mytopic/resetvalues", function(args) {
 var list = [
       {label : 'Alabama', value : 'AL'},
       {id : 'bar',label : 'California', value : 'CA'},
       {label : 'New York', value : 'NY', selected : true},
       {label : 'Texas', value : 'TX'}	           
    ];
  jmaki.publish("/combobox/setValues",  list );
});

As you can see using jMaki actions simplifies the communication between widgets. Something that I didn't show in this blog is calling multiple actions at one time. You also have the option to specify an array of action objects so that multiple events are fired with just one click. One final note, it is also possible to chain actions. I can specify an action which in turn causes another action to be executed. An example would be a button could drive the selection of a combobox which in turn can drive another combobox. It is quite easy to build an interactive web application using jMaki.



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds