Back to my page

Developer Blog



Converting a MiniAPI module to a UWA widget (part 7)

We’ve seen how to switch to UWA pretty all available MiniAPI functionalities in this series’ previous articles. Now is the time to touch on functionalities that cannot be switch as-is, or simply put, that are not available in the stricter, more portable Universal Widget API.

One of the toughest of these is preferences. Sure, we’ve seen how to convert your MiniAPI configuration form into a UWA preference form, but there is one issue that seem to annoy switchers: UWA preferences are not as configurable as MiniAPI. That is, you cannot design them the way you’d do with classic HTML forms: UWA preferences are XML data that are parsed and display in a straight manner, without fuss.

Thing is, some developers really do miss the “fuss”: being able to place text fields side by side instead of on top of each-other, using an image for a given preference, coloring the background… All this could be done in MiniAPI, simply because the configuration form was plain HTML.

In UWA, preferences are set using XML, and in order to be truly portable, we had to let go the ability to customize the Edit form.

But that doesn’t mean that you still can’t get your very own stylish form - only not by using the widget:preferences, but your own tags inside the the body tag and handling it using JavaScript, just as MiniAPI used to do.

Once again, a fearless user leads the way

This is exactly what UWA developer Philip Hansen understood when converting his Daily Comics MiniAPI module to the Revised Daily Comics UWA widget.

See, Philip’s module used its configuration form to let the user choose the comics to display, among a rich list. That list was long enough with every comic placed side-by-side, but had he had to use the widget:preferences XML model, it would simply have gotten twice as long, and hard to manage for the user. So Philip basically took his old configuration code from his old MiniAPI module, and implemented it into UWA.

See the result: when the preferences are folded…

Revised Daily Comics

…and when they are expanded…

Revised Daily Comics, with preferences show

(As you can see, they are so long we had to cut a bit of it. Test them on the widget’s Ecosystem page)

Building the custom preference form

Philip’s form-building code is rather complex, because a has some special needs, but the main points are thus:

  • the form is encapsulated in a div tag with the “comicform” class attribute
  • the form is hidden at loading time through CSS: div.comicform { display: none; }
  • a “Show/Hide Preferences” text is made into an empty link that shall be handled through JavaScript
  • a “Save” button that actually saves all settings for next loading

Making sure settings are saved/retrieved

UWA’s preferences are saved to Netvibes’ server (or a local cookie if not available) every time the form is validated by the user. This way, the next time the widget is loaded, it also loads its previous settings. The preference form is the only one to have its content saved: any other form, created within the widget’s body, would have a blanc/default display on the next loading, whatever its content was.
A way to implement UWA’s preference saving rule into a custom widget is to apply the documented widget.setValue(name, value) method for each preference tag, and on loading the page, to look for data using the widget.getValue(name) method.

Philip’s widget does just this: on loading, it checks if a given setting is set, and applies it to the displayed widget…

for (comic in RevisedComics.fixed) {
  var sname=RevisedComics.shortname(comic);
  RevisedComics.addinput(comic,sname);
  if (widget.getValue(sname) != "true") continue;
  RevisedComics.createspan(comic,sname);
  RevisedComics.cdata[sname]['imgurl'] =
    RevisedComics.fixed[comic];
  RevisedComics.igen(sname);
}

(of course, this code only applies to Philip’s widget, which only uses checkbox, and therefore only needs to check for a true/false value. A more advanced form would require more advanced tests…)

…and on click on the form’s “Save” button, it saves all the checked boxes’ states:

RevisedComics.submit.onclick = function() {
  inputs = widget.body.getElementsByClassName('checkboxes')
  for (var i = 0; i < inputs.length; i++) {
    if (inputs[i].checked == true) {
      widget.setValue(inputs[i].name, "true");
    } else {
      widget.setValue(inputs[i].name, "false");
    }
  }
  RevisedComics.form.style.display = 'none';
  RevisedComics.refreshfunc();
}

Note that RevisedComics.refreshfunc() is the very method that builds check for set values and builds the content accordingly: hence, as soon as the custom form is saved, the content is updated, just as you would expect from a standard UWA preferences form.

Replicating the show/hide user interface

The “Show/Hide Preferences” link is pretty easy to set up, thanks to the combination of JavaScript and CSS. All we have to do is display the form if it’s hidden, and hide it if it’s not. Here is the whole code:

RevisedComics.link =
  widget.body.getElementsByClassName('preflink')[0];
RevisedComics.link.onclick = function() {
  if (RevisedComics.form.style.display == 'block') {
    RevisedComics.form.style.display = 'none';
  } else {
    RevisedComics.form.style.display = 'block';
  }
}

The widget.body.getElementsByClassName('preflink')[0]; call targets this line of HTML, assigning it the JavaScript behavior we have just described, using the tag’s class attribute:

&lt;a class="preflink" href="javascript:void(0)">
  Show/Hide Preferences&lt;/a>

Being creative

While Philip’s code is pretty complex due to internal constraints, his custom preferences form is actually pretty quick to set up. From there on, you can imagine implementing anything you want, and keeping with the style of your MiniAPI configuration form.

Just remember that there is a good reason why a moved from an HTML-based configuration form to a XML-based one between MiniAPI and UWA: having your form in the body of your widget might prove not to be as portable a true UWA preference form would. Be sure to test your code thoroughly, and to handle as many platforms as you can.

Tags: , , , , , ,

6 Responses to “Converting a MiniAPI module to a UWA widget (part 7)”

  1. jeremy Says:

    Currently I’m doing the same on my TV Shows widget. Mainly to deal with internationalization. The only difference was that I keep hidden fields on my preferences definition to save data. What I can see from this point of view is that we don’t have to store data in hidden preferences fields ?

  2. Xavier Says:

    Hey Jeremy, could you be more precise about your final question? If you intend to have them saved with you widget’s configuration, custom hidden fields should also be stored using widget.setValue(). But your method of putting these hidden fields in the standard UWA widget:preferences format can relieve you from some work :)

  3. jeremy Says:

    Well, what I mean is actually in my widget, I have a preferences part with all the user params I want to store in hidden fields:

    [widget:preferences]
    [preference name=”period” type=”hidden” defaultValue=”7″ /]
    [/widget:preferences]

    In my custom preferences form, when I save, I use:

    widget.setValue(’period’, period);

    to store in my hidden fields. My question is: is it useless to add the widget:preferences part in my widget or is it just another security to be sure that my data are stored in a correct UWA way?

  4. Xavier Says:

    Yes, using widget.setValue(name, value) will save the value, even if the widget has no preference of this name.

  5. jeremy Says:

    Now that I implemented this method. It seems that something is a little annoying. When you add a UWA widget with custom preferences into your universe, everyone can acces to the edit panel (of course), but it seems like the options aren’t saved. Can you confirm this?

    It can be a little unsafe if we imagine a developper putting login/password into his custom preferences part. Of course the developper needs to deal with it but is there a way to detect if we are in a universe page or not?

  6. Xavier Says:

    Yes, there is a way. It is the subject to a forthcoming blogpost, but I’ll give directly give you the end of the story: widget.readOnly :) This Netvibes-specific property is logically set to true on Universes, and to false anywhere else (where the widget is supposed to be able to read & write data, and not only display it).

Leave a Reply