sun.com docs.sun.com My Sun Worldwide Sites

  Previous Contents Next

Loading Your Own Data Into a jMaki Widget

Project jMaki gives you a lot of flexibility with respect to how you populate your widgets with data. Following are the three basic techniques for loading data into your widget:

  • Reference a static file that contains the JSON data.

  • Use an EL expression from the tag's value attribute to reference the data from a bean.

  • Use the tag's service attribute to reference data served by a JSP page or servlet.

This tutorial shows you how to use an EL expression from the value attribute to access data from a bean, just as you would with any other JSP tag. Whichever method you choose, you need to be sure that you pass the data in JSON format to the widget because this is what all the jMaki widgets expect. This means that if you have the data in a bean, you need to convert it from a Java object to JSON. jMaki includes the JSON APIs, which you use to perform the data conversion.

The simplejMaki example uses a Dojo combobox widget on its simpleCombobox.jsp page and a Dojo table widget on its tableData.jsp page. Both of these widgets obtain their data from a JavaBeans component. This section explains how these widgets obtain their data from the bean using EL expressions.

Loading Data into a Combobox Widget

The simpleCombobox.jsp page includes a combobox widget that gets its data from a bean, as shown by the jsp:useBean and widget tags from this page:

<jsp:useBean id="appBean" scope="session"
    class="simplejMaki.ApplicationBean" />
<a:widget name="dojo.combobox" ... 
    value="${appBean.stateData}"/>

As the preceding markup shows, the data comes from the getStateData method of ApplicationBean. In ApplicationBean, the getStateData method converts Java String arrays into a single JSON array:

private String[] westernStates = new String[] {
    "Alaska", "Arizona", "California", "Hawaii"};
private String[] westernStateCapitals =
    new String[] {
        "Juneau", "Phoenix", "Sacramento", "Honolulu"};

public JSONArray getStateData() {
    JSONArray statesData = new JSONArray();
    JSONArray stateData = new JSONArray();
    for (int loop=0; loop < westernStates.length; loop++){
        stateData.put(westernStates[loop]);
        stateData.put(westernStateCapitals[loop]);
        statesData.put(stateData);
        stateData = new JSONArray();
    }
    return statesData;        
}

The getStateData method uses the JSON Array API to create the following JSON array:

[['Alaska','Juneau'],['Arizona','Phoenix'],
    ['California','Sacramento'],['Hawaii','Honolulu']]

Using the ${appBean.stateData} expression, the combobox widget loads this JSON array.

Loading Data into a Table Widget

The tableData.jsp page includes a Dojo table widget that also gets its data from a bean. As the default value in the table widget's widget.json file shows, the table widget expects a JSON object:

{"columns": { "title" : "Title", 
              "author": "Author", 
              "isbn": "ISBN #", 
              "description": "Description"},
 "rows": [
    ['JavaScript 101', 'Lu Sckrepter','4412', 
        'Some long description'],
    ['Ajax with Java', 'Jean Bean','4413', 
        'Some long description']
 ]
}

As shown by the preceding markup, the table widget expects a JSON object (denoted by the outer curly braces). This object must contain another JSON object that represents the columns of the table (denoted by the inner set of curly braces) and an array representing the rows of data (denoted by the square brackets). Inside the rows array is a set of other arrays. Each one of those arrays represents a single row of data.

To convert data to this format, the simplejMaki example includes a Book class that represents a single book and two methods in ApplicationBean to create the book data and convert it to JSON format. The following code is a piece of the Book class:

public class Book {

    private int bookId;
    private String title;
    private String firstName;
    private String surname;

    /** Creates a new instance of Book */
    public Book(int bookId, 
                String title, 
                String firstName, 
                String surname) {
        this.bookId = bookId;
        this.title = title;
        this.firstName = firstName;
        this.surname = surname;
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }
    ... // other getter and setter methods for the other properties.

The following code shows the createBooks method, which creates a set of book data, and the getBookData method, which converts the book data to JSON format.

public List createBooks() throws Exception {
    ArrayList books = new ArrayList();
    Book book = 
        new Book(201, 
           "My Early Years: Growing up on *7", 
           "Duke", "");
    books.add(book);
    book = 
        new Book(202, 
            "Web Servers for Fun and Proft", 
            "Jeeves", "");
    books.add(book);
    book = 
       new Book(203, 
            "Web Components for Web Developers", 
            "Webster", "Masterson");
    books.add(book);
    return books;
}
...

public JSONArray getBookData() throws Exception {
     JSONArray books = new JSONArray();
     JSONArray book = new JSONArray();
     ArrayList bookList = ArrayList)createBooks();
     Iterator i = bookList.iterator();
     while(i.hasNext()){
         Book bookData = (Book)i.next();
         book.put(bookData.getBookId());
         book.put(bookData.getTitle());
         book.put(bookData.getFirstName());
         book.put(bookData.getSurname());
         books.put(book);
         book = new JSONArray();
    }
    return books;
}

The getBookData method, like the getStateData method referenced by the combobox widget, creates a JSON array that contains the book data:

[
 ['201','My Early Years: Growing up on *7', 'Duke',''],
 ['202','Web Servers for Fun and Profit','Jeeves',''],
 ['203','Web Components for Web Developers','Webster','Masterson']
]

Notice that getBookData does not create the column data and returns only the row data. This is because the JSONObject API uses HashMap under the covers. As you might know, HashMap does not guarantee insertion order. In this case, the example requires insertion order so that the column headings match up with the row data. Therefore, you need to reference the column data separately from the page, either by entering it directly in the tag or by referencing a String variable that is in the proper JSON format. The following tag represents the Dojo table on the tableData.jsp page:

<a:widget name="dojo.table" 
    value="{columns: {'isbn':'ISBN #',
                      'title':'Title',
                      'firstName':'First Name',
                      'surname':'Last Name'},
           rows:${appBean.bookData}}"
/>

Now that the getBookData method returns the row data in JSON format, the tag can reference the method from the rows attribute to get the data. In this case, the columns attribute specifies the column headings in JSON object format.

Loading Content from a URL

Some of the widgets that jMaki offers are meant to be containers for other content. These widgets include the Dojo tabbed pane and the Spry accordion. The jMaki project also offers a native widget (a widget not created from third-party code) called a dContainer, whose purpose is to load content from URLs.

The tabbed pane, accordion, and dContainer widgets all use the jMaki Injector API under the covers to load content from a separate URL that is hosted within the same domain. You can learn more about how the Injector API works from the document jMaki Injector.

The simplejMaki example uses dContainer to load a particular page based on which fisheye widget image the user clicked. The index.jsp page includes the following dContainer widget:

<a:widget name="jmaki.dcontainer" 
          args="{topic:'/jmaki/centercontainer', iframe:true}"/>

The next section discusses what the topic attribute is. The other attribute, iframe, is true because the simplejMaki example uses a Yahoo map widget. This widget uses document.write when rendering the page, thereby possibly overwriting content in the page. By setting the iframe attribute to true, you are wrapping the widget in a container similar to an IFrame so that rendering happens correctly. An IFrame is an HTML element that allows you to embed an HTML document inside another HTML document.

Now let's go back to the fisheye widget to find out how content is loaded by way of the dContainer widget in this example. The items attribute of the fisheye widget tag references each of the icons in the fisheye and identifies which URL should be accessed when the user clicks the corresponding icon:

<a:widget name="dojo.fisheye" 
          args="{labelEdge:'right', 
                 items:[
                     {'iconSrc':'web/images/menu.JPG',
                      'url':'simpleCombobox.jsp',
                      'caption':'Combobox Example!'},
                     {'iconSrc':'web/images/map.JPG',
                      'url':'comboboxGeocoder.jsp',
                      'caption':'Map Example'},
                     ...], 
                 orientation:'vertical'}"/>

To load one of the URLs referenced by the fisheye widget, the dContainer needs to listen for the event of a user clicking one of the fisheye widget icons. The next section explains how to use the jMaki publish and subscribe mechanism to publish events to a topic, register listeners to handle an event, and subscribe to a topic.

Handling Widget Events

Project jMaki supports a publish/subscribe system that makes it easy for your application to respond to widget events and to get two widgets to interact by listening for each other's events. All the widgets included with Project jMaki publish certain of their attributes to topics when users initiate events on the widget. This section describes how your application can subscribe to a topic so that it can respond to a widget event. It also explains how you can get one widget to respond to the events fired by another widget.

The combobox widget publishes its value to a topic when the widget experiences an onChange event, as shown in the component.js file for the combobox widget:

var topic = "/dojo/combobox";
this.onChange = function(value){
    jmaki.publish(topic, value);
}

You can change the topic using the topic argument of the widget tag representing the combobox widget, as shown by this widget tag from the simpleCombobox.jsp page:

<a:widget name="dojo.combobox" 
          args="{topic:'/dojo/combobox/value'}" 
          value="${appBean.stateData}"/>

Now you can write some listener code that subscribes to the topic to get the current value and do something based on the value.

You can include the listener code directly in your page. The simplejMaki example includes all listener code in either the glue.js file or the resources/system-glue.js file. It also programmatically registers the listeners with the jMaki server runtime in the glue.js file. Alternatively, you can declaratively register listeners in the config.json file, as described in jMaki Glue.

The following code is from the glue.js file, located in the web directory of the simplejMaki example project:

jmaki.addGlueListener("/dojo/combobox/value", "jmaki.listeners.getValue");

jmaki.listeners.getValue = function(args) {
    var targetDiv = document.getElementById("newvalue");
    if (targetDiv)
        targetDiv.innerHTML = "The capital of this state is " + args.value;
}

The preceding code does two things:

  • It registers the jmaki.listeners.getvalue function under the topic name /dojo/combobox/value. The registered function must be called when the value is published to the topic.

  • It defines the jmaki.listeners.getvalue listener function. In this case, the function writes out to the newvalue div element on the simpleCombobox.jsp page a message saying what the capital of the selected state is.

You can also add listener code that allows one widget to listen for events of other widgets. As the previous section mentioned, the dContainer widget listens for the event of a user clicking an icon in the fisheye widget. In this case, the fisheye widget uses the default /dojo/fisheye topic that is already set in the fisheye widget code.

The dContainer widget instance included in index.jsp listens to the /jmaki/centercontainer topic for updates to the URL argument because its topic attribute specifies the /jmaki/centercontainer topic:

<a:widget name="jmaki.dcontainer"
          args="{topic:'/jmaki/centercontainer', iframe: true}"/>

Now, you need to provide some listener code in glue.js to get the dContainer to load the URL associated with the icon the user selected from the fisheye widget.

jmaki.addGlueListener(new RegExp("^(?!/global).*/dojo/fisheye"), 
    "jmaki.listeners.handleFisheye");

jmaki.listeners.handleFisheye = function(args) {
    jmaki.publish("/jmaki/centercontainer", args.target.url);
}

The preceding code registers the handleFisheye function in the jmaki.listeners namespace. When the user clicks an icon in the fisheye widget, the arguments associated with that icon are published to the /dojo/fisheye topic. When that happens, the handleFisheye function is called with the list of arguments.

The handleFisheye function calls the jMaki publish function, which publishes the URL argument to the /jmaki/centercontainer topic. Because the dContainer instance's widget tag specifies the /jmaki/centercontainer topic, the widget is listening to this topic for updates to the URL argument. If the URL argument is set, the dContainer widget loads the specified URL.

You can also use the glue.js and system-glue.js files in combination with the jMaki XmlHttpProxy client to interact with services in another domain, which the next section describes.

Previous Contents Next
Company Info Contact Terms of Use Privacy Copyright 1994-2007 Sun Microsystems, Inc.