Writing Google Gadgets – A Tutorial – Part 03

Archived Content
This article is provided for historical perspective only, and may not reflect current conditions. Please refer to relevant product page for more up-to-date product information and resources.
  • By Samisa Abeysinghe
  • 21 Nov, 2011

Writing Google Gadgets – A Tutorial – Part 03

In the previous two parts of this tutorial, we have studied basics of Google gadgets, dealing with HTML and using third party
JavaScript libraries to make the presentation appealing.

In this third part of this tutorial, we will dive into the details of setting user preferences, and explore how to use user
preferences to make the same gadget more versatile and powerful.

Writing Google Gadgets – A tutorial – Part 04

        Versatility of Google Gadgets as a Presentation Instrument

        The Complete Google Gadget Code

        Tips and Tricks

                Error Handling

                Incremental Development

                When Things go wrong: F12

                Always Test with IE

                Use an IDE


Setting User Preferences

When we discussed the anatomy of a gadget in part 01 of this tutorial, we discussed the <UserPrefs>
section and also had a glimpse of setting a preference with the Hello gadget. Let’s use the MarkMail gadget that we developed so far
to set the mailing list as a user preference, so that we can use the same gadget, to display graphs for various lists.

For this, we can add a simple option where the user can set the URL of the list and we can pick that user set option and fetch data accordingly.

    <UserPref name="MailingList" 
         display_name="Mailing List"
         default_value="http://markmail.org/browse/org.wso2.carbon-commits">
    </UserPref>

The user preference allows the user to set the URL of the mailing list.
And in the gadget logic, we can use the value of the preference set, rather than the hard coded URL value.
For that we need to first initialize the preferences, in our Javascript logic, within the gadget.

        var prefs = new gadgets.Prefs();

And then, use the "prefs" object to pick the value set for "MailingList"

        var url = prefs.getString("MailingList");  
        gadgets.io.makeRequest(url, processResponse, parameters);

That is it! The rest of the gadget logic is the same as we used it last time.

Note that, by default, the type of the preference is treated as a string.
Hence we use the "getString" API, to retrieve the value of the user preference.

There are several data types supported by Google gadget API for the user preference section [1].
In addition to that, the gadgets.Prefs class API provides means to get and set user preferences programmatically using JavaScript [2].

MarkMail Gadget User Preferences

Now the user can use the settings menu icon on gadget’s title bar and set a mailing list address. And the gadget will reload with new data.


Using enum Data Type as an Options List

While allowing the user to set any URL using a string preference, it can also lead to errors, and even usability concerns if the users do not know where to find the right URLs.

Hence it is more useful to provide the list of URLs that we are interested in as a list of options.
Also, based on the URL selected, we need to change some setting in the gadget presentation, for example the caption of the table.

So we need some more logic in place, rather than just using a simple string box, to pick the URL.

Let’s say we want this gadget to support visualizing data for all major WSO2 open source mailing lists.
So we can allow users to pick a mailing list with an instance of the gadget. For this we can use the "enum" type user preference.

    <UserPref name="MailingList" display_name="Mailing List"
        datatype="enum" default_value="3">
        <EnumValue value="0"
            display_value="WSO2 Architecture List - http://markmail.org/browse/org.wso2.architecture" />
        <EnumValue value="1"
            display_value="WSO2 Carbon Dev List - http://markmail.org/browse/org.wso2.carbon-dev" />
        <EnumValue value="2"
            display_value="WSO2 Stratos Dev List - http://markmail.org/browse/org.wso2.stratos-dev" />
        <EnumValue value="3"
            display_value="WSO2 SVN Commits - http://markmail.org/browse/org.wso2.carbon-commits" />
        <EnumValue value="4"
            display_value="WSO2 Carbon Jira Activity - http://markmail.org/browse/org.wso2.carbon-jira" />
        <EnumValue value="5"
            display_value="WSO2 Stratos Jira Activity - http://markmail.org/browse/org.wso2.stratos-jira" />
    </UserPref>

In this enum user preference, we have used values 0 to 5 and a descriptive text for each as display value. Then we can pick the user selection using:

        var listID = prefs.getString("MailingList");  

Now based on the value of "listID", which is a value between 0 to 5, matching the enum values, we can use some JavaScript logic to pick other variables such as column names,
caption and the mailing list URL. For e.g. we can pick the table title and column title, for the graph using the combination of a JavaScript arrays and selected list ID value:

	  var titles = new Array("WSO2 Architecture List Discussions", 
							 "WSO2 Carbon Dev List Discussions",
							 "WSO2 Stratos Dev List Duscussions",  
							 "WSO2 Carbon and Stratos SVN Commits",
							 "WSO2 Carbon Jira Activity", 
							 "WSO2 Stratos Jira Activity");

	  var columns = new Array("Discussions", "Discussions", "Discussions",
							  "Commits", "Activity", "Activity");

	  var listID = prefs.getString("MailingList");

	  document.getElementById('contentDiv').innerHTML = "<table id='data'><caption>" + 
														  titles[listID] + 
														  "</caption><thead><tr><th></th><th scope='col'>" +
														  columns[listID] + 
														  "</th></tr></thead>" + 
														  document.getElementById('browse').getElementsByTagName('table')[0].innerHTML + 
														  "</table>";

Note that, the selected value of enum can be accessed both using getString and getInt, given the value field contains a number. But, if it contains non-digit characters, using getInt will result in an error.


Using Boolean Data Type

In the gadget, we display only the graph. But we also have the data as a HTML table.
If the user wish, we can allow the user to see the table instead of the graph.

This will allow the user to duplicate the same gadget and see the data and the graph side by side.

MarkMail Gadget Graph and Table

This can be achieved with a simple Boolean type user preference.

    <UserPref name="ShowGraph" display_name="Show Graph"
        datatype="bool" default_value="true">
    </UserPref>

And JavaScript code that deals with it.

	if (prefs.getBool("ShowGraph") == true) {
		drawGraph();
		document.getElementById('contentDiv').style.display = "none";
		document.getElementById('graphDiv').style.display = "block";
	} else {
		document.getElementById('graphDiv').style.display = "none";
		document.getElementById('contentDiv').style.display = "block";
	}

Note that we can use the getBool API call to access boolean type user options.


Integer Settings with String Data Type

If you carefully look at the user preference documentation [1], you will notice that, there is no integer data type,
but the API supports getInt [2].

What this means is that, if you want to deal with integer type user option, such as the height and width of the graph,
you can use a string type user preference and access the value using getInt.

    <UserPref name="GraphWidth" display_name="Graph Width"
        default_value="600">
    </UserPref>
    <UserPref name="GraphHeight" display_name="Graph Height"
        default_value="625">
    </UserPref>

And JavaScript code that deals with it.

	var g = new Bluff.SideBar('graph', prefs.getInt("GraphWidth") + 'x' + prefs.getInt("GraphHeight"));


Setting Preferences from Within the Gadget

User preference setting functionality is built into the gadget framework, and the API is easy to use.
You just define the user preferences in the gadget, and the preferences will be generated with a form,
inside the settings action area of the gadget, by the framework.

For most of the options, that are usually one off, this is useful, and once set, will be hidden away from user,
without getting in the way of the visual area.

But there could be some user preferences, which might need to be changed more frequently.
For example, as opposed to the mailing list URL, that is a per gadget setting, the theme of the graphs might be more frequently changed, to suite user tastes.

In other words, an admin can lay out the number of instances of the gadget, and what mailing list,
each instance is displaying data about, but the graph theme might be a user specific choice.

For more frequently changed options, such as the theme of the graphs, having only the default user preference form could be a productivity killer.
This is because, the user has to go through 4 clicks each time a change is required, as shown in the following diagram.

MarkMail Gadget User Preferences Actions Flow

Hence, for more frequently used options, we can provide a mechanism to set an option with one click, as opposed to four clicks.
For this, we need to do three things, in addition to what we would do for a usual user preference setting.

First, we need to require feature "setprefs" inside <ModulePrefs> element:

    <ModulePrefs title="WSO2 Open Source Development Data from MarkMail">
        <Require feature="dynamic-height" />
        <Require feature="setprefs"/>
    </ModulePrefs>

Second, we need to provide an on screen, in other words, within gadget, HTML input element to allow the user settings.
For our example, we will use a select element for the user to pick the theme.

Grapth Theme: <select id="graphThemeSelect" onchange="if (this.selectedIndex >=0 ) updateTheme(this);">
                  <option value="0">Keynote</option>
                  <option value="1">37signals</option>
                  <option value="2">Rails Keynote</option>
                  <option value="3">Odeo</option>
                  <option value="4">Pastel</option>
                  <option value="5">Greyscale</option>
              </select>

Third and final step is to implement the updateTheme JavaScript fuction, triggered upon onchange event of select element.

	  function updateTheme(theme) {
		  prefs.set("GraphTheme", theme.value);
		  drawGraph();
	  }

Note that, inside the udateTheme fuction, we use the prefs.set API call to set the preference value.
This means, that the preference value will be persisted, and when the user views the gadget next time,
the last set theme will be used, as opposed to the default theme.

If we used just JavaScript without the gadget API, this persistence will not take place, a noteworthy advantage that makes Google gadgets more useful over just HTML.

You can find more information on the use of setprefs feature and saving sate on Google gadgets documentation [3]


Dealing with Views

There is a "view" attribute that is available with <Content> element.
Multiple <Content> elements can be defined to make the gadget look different and fit for various views.

However, defining multiple <Content> elements can lead to gadget logic duplication, especially for one as sophisticated
as the MarkMail gadget that we developed.

However, the good news is that, Google gadget API has provision to deal with views programmatically [4].

For our example MarkMail gadget, we can choose to display both the table and the graphs, side by side on the same gadget,
when the gadget is in "canvas" view. Canvas view is the view you get when the gadget is maximized. In the maximized view,
we have enough real state to show both the table and the graph on the same gadget.

For view API to be used, we need to require views feature.

    <ModulePrefs title="WSO2 Open Source Development Data from MarkMail">
        <Require feature="dynamic-height" />
        <Require feature="setprefs"/>
        <Require feature="views" />
    </ModulePrefs>

Then, if we are in canvas view, we show both content div and graph div.

		if (gadgets.views.getCurrentView().getName() == "canvas" ) {
			drawGraph();
			document.getElementById('contentDiv').style.display = "block";
			document.getElementById('graphDiv').style.display = "block";
		}

Now, here is the maximized view of the gadget.

MarkMail Gadget Canvas View

In this part of the tutorial, we learned the power of user preference settings, and how to make the gadget more versatile.
In the next and final part of this tutorial, a detailed line by line description of the complete gadget will be provided,
along with some tips and tricks to keep in mind when developing gadgets.

References

[1] href="http://code.google.com/apis/gadgets/docs/fundamentals.html#Datatypes">User
preference data types supported by Google gadget API

[2] href="http://code.google.com/apis/gadgets/docs/reference/#gadgets.Prefs">gadgets.Prefs
API to deal with Google gadget user preferences

[3] href="http://code.google.com/apis/gadgets/docs/fundamentals.html#SetPrefs">setprefs
and saving state with Google gadgets

[4] href="http://code.google.com/apis/gadgets/docs/ui.html#curr_view">Determining
current view using Google gadget API




This brings us to the end of this four part tutorial. Please feel free to provide us the feedback, and help us improve the content.

Please refer to the table of contents section to find out about the other parts of this tutorial.

About Author

  • Samisa Abeysinghe
  • VP, Training
  • WSO2 Inc.