Managing data in JQuery UI controllers

It’s common this days to use rich UI in web applications. It seems to be natural to use JavaScript based HTML user interface controllers, rather than old fashion re-loadable  HTML.

If you are familiar with JS based UI you should remark that many of UI frameworks eg. jQueryUI or Dojo Toolkit has limited number of widgets to use out of the box.

If you need standard UI elements like button, accordion, list, grid/table etc.  you will find it ready to use in frameworks. Unfortunately most of enterprise web applications needs dedicated UI widget.

Usually best way is to use existed widgets and create what is needed.

It is easy to start, implement first events, first functionality, but after 200 lines of JS code it can be painful to maintain simple tasks.

I will show you how I manage states and data in some of my jQuery widgets. This is not best way for official widgets, but it is best for simple widgets created for dedicated applications.

Official, more convenient way using jQueryUI Widget were described on jQueryUI widget page. If you want to create official plugins you should check the official way.

In this article I present only simple way for managing states and data of widget.

Separate HTML elements into jQuery Widgets

Main idea is to not use functions for create widget. It is easy to write.

WRONG WAY


function createList(div, data) {
  for (var d in data) div.append(newRow((d)));
  .. handle list
}
function newRow(text) {
  return jQuery(document.createElement("div")).text(d);
  .. handlers of row
}

You can manage one 1-5 methods like this, but not big widget. Do not write functions for widgets, use jQuery extensions instead.

BETTER WAY

jQuery.fn.myList = function (params) {
  for (var d in params.data) {
    var rowData = {
      data: d,
      list: this
    }
    this.append(jQuery(document.createElement("div")).myRow(rowData));
  }
}
jQuery.fn.myRow = function (params) {
  this.text(params.data.text);
}

Did you notice that I separate list and row for two widget? I did it before in functions for code clean.
But now it is important! Not only to keep code clean but to separate objects that will be created.
Notice that I pass list instance to row. This way I will have reference to container in row.
This is one of condition for element to separate as a widget, if you will need instance of other widget to use.

Params

Ok but what about parameters and default widget definition? In code above I pass object into widget definition, not only data that will be used.

Why I did it this way? To open access to all configuration of widget.I recommend this even when you need only simple data, eg. text for row.
This way you will have open code for changes, eg. to add different style, or different click handler in future.

Also default configuration and configuration extensions will should be always defined at the beginning. Final base of widget will look like this

jQuery.fn.myRow = function (params) {
  var myRowData = jQuery.data(this[0], "myRowData"); // STORED WIDGET DATA
  var def = {};
  if (!myRowData) {
    var def = { // DEFAULT CONFIGURATION
      text: "empty",
      click: function () {}
    }
  } else {
    def = myRowData;
  }
  jQuery.extend(def, params); // EXTEND WIDGET
  this.click(def.click); // EVENT
  jQuery.data(this[0], "myRowData", def); // STORE WIDGET DATA
}

Let’s see what will happen after create myRow widget.

Widget will check if this is first call and widget must be defined or widget must be redefine.

var myRowData = jQuery.data(this[0], "myRowData");
...
if (!myRowData) {
...

For first call default definition is provided, otherwise previous definition is retrieved from data of it’s HTML element.
I prefer to use jQuery.data every time when some data, functions or objects must be connected in some how with HTML elements.
This way I never confused to calling object that I want to call. Object scope is very tricky in JavaSctipt.

After getting new or retrieving old definition of widget, new definition param is used to extend it.

jQuery.extends(def, param);

This way you can pass new functions to handle click or other events or even creation of widget it self if you define those in def object.

At the end definition is linked width HTML element. This part could be stored only at the widget creation, but this way is more readable.

  jQuery.data(this[0], "myRowData", def);

And this is it! Simplest way for widget data manage.
Now you should add creation method and call it when widget is not defined, add events that are defined like below.

this.click(def.click);

Changes for different kind of widget

Remember that, in sample above I pass function to event, so if you would like to change it after widget creation it must be defined in different way.

var _this = this;
this.click(function (ev) {
  var myRowData= jQuery.data(_this[0], "myRowData");
  myRowData.click(ev);
});

Other thing is definition of widget that will be applied for more than one element eg. to all SPANs. Then you must change definition and iterate over all elements in this array rather than use this[0]

Final thought

Always try to separate even simplest part of widget like button, row of list, etc into dedicated widgets.

Happy scripting!

Advertisements

One response to “Managing data in JQuery UI controllers

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s