Introduction
In this article, I'm going to teach you how to develop your own custom widget in the iDevUI framework. These are usually called UXs (User eXtensions) and add extra functionality to iDevUI.
Background
iDevUI is a JavaScript framework which sits atop of jQuery. iDevUI enables you to create rich web applications by simply writing them as a set of JavaScript Objects. This enables developers (like me) to create amazing web applications just be simply defining some objects and letting iDevUI do the rest.
To find out more about iDevUI, you can go to the iDevUI website here. If you don't know about iDevUI, I suggest you try it first before you think about extending it with your own custom widget.
iDevUI is released under the MIT license.
The code
So we're going to create our own example widget and I'll show you what tools iDevUI gives you to add to your widget to make it easier for developers to use your widget.
The Setup
So firstly, we need to download a new copy of iDevUI and place it in a folder. Once we have done that, the directory structure should look like this:
css
images
js
- idevui
We're going to create our custom widget inside of the idevui folder. Create a new folder in idevui/ux called examplewidget.
Inside that new folder, we're going to create 2 new blank files: examplewidget.css and examplewidget.js.
The CSS file will hold all of the styling for our new widget and the JS file will hold the code that iDevUI will use to render your widget when the developer writes it in their app.js.
Now we just need to tell iDevUI to load it. To do this open js/preferences.js and inside the ux array add a new row like so:
ux:[
"gauge/gauge.js",
"uploader/uploader.js",
"datepicker/datepicker.js",
"signature/signature.js",
"pictures/pictures.js",
"treeview/treeview.js",
"examplewidget/examplewidget.js"
],
Now iDevUI will know about your custom widget. Lets write it!
Writing the JavaScript
For this article, we're just going to create a widget that will output the current time on render.
To start, open your idevui/ux/examplewidget/examplewidget.js file we created earlier.
Firstly, let's define that we're creating a widget and that it should extend from iDevUI's baseWidget class:
idev.ux.widgetExample = baseWidget.extend(
{
});
iDevUI then requires us to create an init function which initialises the widget:
idev.ux.widgetExample = baseWidget.extend(
{
init: function(config)
{
}
});
As you can see above, an argument gets passed to the init function automatically by iDevUI called config. This config is an object which the developer has defined in their app.js (with defaults added for anything that is missing).
We can use this to setup our own properties for our widget:
idev.ux.widgetExample = baseWidget.extend(
{
init: function(config)
{
this._super(config);
this.wtype = "example";
this.twelvehour = config.twelvehour || false;
this.tpl = new idev.wTemplate(
"<div id='{id}' class='ui-example' style='{elementstyle}{style}'>",
"{clock}",
"</div>"
);
}
});
For now, we're just going to have 1 properly called twelvehour
which will set whether the clock is 12 hour rather than 24. By default the clock will be 24 hour.
The tpl
property is an iDevUI wTemplate that defines our template for us to render. It uses tags (surrounded by {}) which get filled in using the array we will be passing to it.
Another thing iDevUI requires is a render method. This method gets called when iDevUI wants to render your widget:
idev.ux.widgetExample = baseWidget.extend(
{
init: function(config)
{
this._super(config);
this.wtype = "example";
this.twelvehour = config.twelvehour || false;
this.tpl = new idev.wTemplate(
"<div id='{id}' class='ui-example' style='{elementstyle}{style}'>",
"{clock}",
"</div>"
);
},
render: function()
{
if (this.renderTo == null) return;
var data = new Array();
data['id'] = this.id;
data['width'] = this.width;
data['height'] = this.height;
data['elementstyle'] = this.elementstyle;
data['style'] = this.style;
data['clock'] = this.generateClock();
var sHTML = this.tpl.render(data);
$("#" + this.renderTo).append(sHTML);
this.rendered = true;
}
});
This render method will now successfully render our widget. However, we need to create the generateClock() method to provide the current time. Let's write that.
idev.ux.widgetExample = baseWidget.extend(
{
init: function(config)
{
this._super(config);
this.wtype = "example";
this.twelvehour = config.twelvehour || false;
this.tpl = new idev.wTemplate(
"<div id='{id}' class='ui-example' style='{elementstyle}{style}'>",
"{clock}",
"</div>"
);
},
render: function()
{
if (this.renderTo == null) return;
var data = new Array();
data['id'] = this.id;
data['width'] = this.width;
data['height'] = this.height;
data['elementstyle'] = this.elementstyle;
data['style'] = this.style;
data['clock'] = this.generateClock();
var sHTML = this.tpl.render(data);
$("#" + this.renderTo).append(sHTML);
this.rendered = true;
},
generateClock: function()
{
var d = new Date();
var ampm;
var time;
var hours = d.getHours();
var minutes = d.getMinutes();
if(hours < 12)
{
ampm = "AM";
}
else
{
ampm = "PM";
}
if(this.twelvehour)
{
if(hours == 0)
{
hours = 12;
}
if(hours > 12)
{
hours = hours - 12;
}
}
if(minutes.length == 1)
{
minutes = "0" + minutes;
}
if(this.twelvehour)
{
time = hours + ":" + minutes + " " + ampm;
}
else
{
time = hours + ":" + minutes + " " + ampm;
}
delete d;
return time;
}
});
Now we have a successfully generated time which will be generated on render. However, this is still not a complete widget, widgets in iDevUI have their own custom public methods and events attached. Let's add those:
idev.ux.widgetExample = baseWidget.extend(
{
init: function(config)
{
this._super(config);
this.wtype = "example";
this.twelvehour = config.twelvehour || false;
this.tpl = new idev.wTemplate(
"<div id='{id}' class='ui-example' style='{elementstyle}{style}'>",
"{clock}",
"</div>"
);
},
render: function()
{
if (this.renderTo == null) return;
var data = new Array();
data['id'] = this.id;
data['width'] = this.width;
data['height'] = this.height;
data['elementstyle'] = this.elementstyle;
data['style'] = this.style;
data['clock'] = this.generateClock();
var sHTML = this.tpl.render(data);
if(this.events && this.events.beforerender) this.events.beforerender(this);
if(this.events && this.events.beforeRender) this.events.beforeRender(this);
$("#" + this.renderTo).append(sHTML);
if(this.events && this.events.afterrender) this.events.afterrender(this);
if(this.events && this.events.afterRender) this.events.afterRender(this);
this.rendered = true;
},
generateClock: function()
{
var d = new Date();
var ampm;
var time;
var hours = d.getHours();
var minutes = d.getMinutes();
if(hours < 12)
{
ampm = "AM";
}
else
{
ampm = "PM";
}
if(this.twelvehour)
{
if(hours == 0)
{
hours = 12;
}
if(hours > 12)
{
hours = hours - 12;
}
}
if(minutes.length == 1)
{
minutes = "0" + minutes;
}
if(this.twelvehour)
{
time = hours + ":" + minutes + " " + ampm;
}
else
{
time = hours + ":" + minutes;
}
delete d;
return time;
},
setTwelveHour: function()
{
this.twelvehour = true;
idev.dom('#' + this.id).html(this.generateClock());
},
setTwentyFourHour: function()
{
this.twelvehour = false;
idev.dom('#' + this.id).html(this.generateClock());
}
});
Now we have 2 events, afterRender
and beforeRender
and we have 2 custom methods which the developer can call, setTwelveHour
and setTwentyFourHour
.
Finishing touch
Finally, let's register this as a widget in iDevUI and add our CSS styling.
idev.ux.widgetExample = baseWidget.extend(
{
init: function(config)
{
this._super(config);
this.wtype = "example";
this.twelvehour = config.twelvehour || false;
this.tpl = new idev.wTemplate(
"<div id='{id}' class='ui-example' style='{elementstyle}{style}'>",
"{clock}",
"</div>"
);
},
render: function()
{
if (this.renderTo == null) return;
var data = new Array();
data['id'] = this.id;
data['width'] = this.width;
data['height'] = this.height;
data['elementstyle'] = this.elementstyle;
data['style'] = this.style;
data['clock'] = this.generateClock();
var sHTML = this.tpl.render(data);
if(this.events && this.events.beforerender) this.events.beforerender(this);
if(this.events && this.events.beforeRender) this.events.beforeRender(this);
$("#" + this.renderTo).append(sHTML);
if(this.events && this.events.afterrender) this.events.afterrender(this);
if(this.events && this.events.afterRender) this.events.afterRender(this);
this.rendered = true;
},
generateClock: function()
{
var d = new Date();
var ampm;
var time;
var hours = d.getHours();
var minutes = d.getMinutes();
if(hours < 12)
{
ampm = "AM";
}
else
{
ampm = "PM";
}
if(this.twelvehour)
{
if(hours == 0)
{
hours = 12;
}
if(hours > 12)
{
hours = hours - 12;
}
}
if(minutes.length == 1)
{
minutes = "0" + minutes;
}
if(this.twelvehour)
{
time = hours + ":" + minutes + " " + ampm;
}
else
{
time = hours + ":" + minutes;
}
delete d;
return time;
},
setTwelveHour: function()
{
this.twelvehour = true;
idev.dom('#' + this.id).html(this.generateClock());
},
setTwentyFourHour: function()
{
this.twelvehour = false;
idev.dom('#' + this.id).html(this.generateClock());
}
});
idev.ux.loadCSS("examplewidget/examplewidget.css");
idev.register("example",idev.ux.widgetExample);
.ui-example {
border: 2px dashed red;
}
And we're done! We have a full working widget. Obviously this is just an example and the clock will only show the time of the render and won't update every minute.
Using the widget
Now I'm going to show you what the developer has to do to use your widget.
First they have to download your widget folder from your ux folder (called example widget) and place it in their ux folder. Then they have to update their preferences (in the same way we did above).
Now they can open their js/app.js and add your widget to their page:
{
wtype: 'example',
twelvehour: true
}
{
wtype: 'example',
events:{
beforerender: function(wgt){
wgt.setTwelveHour();
}
}
}
Conclusion
This is a really powerful way to creating new custom widgets in iDevUI and allows you to create amazing new widgets for iDevUI developers.