The Widget Factory
Total Page:16
File Type:pdf, Size:1020Kb
The Widget Factory Version 1.8 of jQuery UI split the functionality that allows widgets to be easily created into a separate and standalone utility file. This is the jquery.ui.widget. js file and we can use it to create our very own jQuery UI plugins with ease. Like jQuery itself, which provides the fn.extend() method for easily creating plugins, jQuery UI also provides mechanisms to make plugin creation easier and to ensure that the common API functionality is retained in new plugins. In this chapter, we will cover the following topics: • The benefits of using the Widget Factory to write plugins • Creating and invoking widget methods • Resetting a widget • Creating an example calculator widget • Adding options to our example widget • Making widgets themeable • Supporting the common API methods • Adding methods and responding to events in our widget • Using our widget in a web page Introducing the Widget Factory If you've spent some time working with jQuery, you will no doubt have created a plugin of some description, or have used ones already created by others, as part of developing with the library. The recent jQuery plugins are stateless, where the extent of the interaction is limited to us calling them on an element. In most cases, this works perfectly well, but there's a large set of functionality that doesn't fit into the basic plugin pattern. The Widget Factory To help fill this gap, jQuery UI has implemented a more advanced plugin system. The new system manages state, allows multiple functions to be exposed via a single widget, and provides various extension points. This system is called the Widget Factory and is exposed as jQuery.widget as part of jQuery UI. Reasons for using the Widget Factory When writing jQuery plugins, we would normally add or override a property or method within jQuery.prototype (or $.fn, as it is more commonly written), and follow some conventions such as returning this, to allow the plugin to be chained with others within our code. So what is the Widget Factory all about, and why should we use something that is hundreds of lines long? There are two main reasons why we should use the factory: • Eliminate the need to produce a lot of boilerplate code for the plugin: In most cases, jQuery plugins are stateless; they perform a single action on an element and don't need to be preconfigured. In these instances, we would normally extend jQuery's prototype. If a plugin is stateful, it requires code to manage state and react to changes. The Widget Factory is a means to help the abstract code for common tasks such as creating or destroying widgets into a clear, consistent format that can be used across all jQuery UI plugins. • Produce a consistent API: The Widget Factory creates a common standard framework that you use to create and destroy widgets, get and set options, and invoke methods, of which a large part of this is already implemented by the Factory. Using a defined standard makes it easier for others to use your widgets. In summary, the Widget Factory also provides a number of benefits that we can utilize when developing widgets. These include the following: • We can create our own namespace rather than using the default of ui, which is not a good practice (jQuery.al) • The widget can use its own class (jQuery.al.filterable.prototype) and extend the existing jQuery prototype (jQuery.fn.filterable) • We can create our default settings, then merge-in any user-defined settings • We can use the .data() method to store plugin instance in .data() • Methods are accessible via use of string—plugin( "foo" ) or directly .foo() • The factory helps prevent against multiple instantiation of the same widget [ 2 ] Chapter 15 • The structure for implementing creation, destruction, and changes within the plugin has been written as a series of events, so we can break the widget code into a series of smaller functions, making it easier to manage • We can easily expose callbacks using the ._trigger method such as _trigger( "event" ) • We can use this to maintain proper scoping of elements throughout the plugin Let's now take a look at how to create the basic skeleton of your widget file, which we will use later in this chapter to create an example calculator widget. Creating a skeleton plugin When using the Widget Factory to create widgets within jQuery UI, you will notice that there are a number of common methods that will be used in the code. Let's take a look at each of them in more detail. We begin with creating the namespace for our widget; this should use a custom name in the format of <custom-ui-name>.<name-of-widget>. The namespace will be created automatically, if it doesn't already exist. We also need to set the options that will be available to our end users, and which they can use to change the widget's configuration: ( function($,undefined) { $.widget( "al.progressbar", { // These options will be used as defaults options: { className : "" }, Next comes the initialization method for the widget; here is where we set up the widget when it is called from within our web pages: _create: function() { ... }, We then need to implement our various methods that users can then call upon when using the widget in their code: filter: function() { ... }, [ 3 ] The Widget Factory There will be methods that we need to implement in our widget, the scope of which is only intended to be within the widget itself and not for general consumption. We can implement these in the same manner as the public methods, but with the addition of the underscore before the name to denote a private method: _hover: function() { ... }, Unless our widget only performs one operation or doesn't need to allow the user to change its configuration, we will very likely need to configure the widget with some options. We will have already created the array of options at the beginning of the plugin, we now need to respond to those option changes and process any requests. We start with _SetOptions, which is called anytime the option() method is used: _setOptions: function (options) { this._superApply(arguments); }, This in turn calls _SetOption, which processes each option change before calling the parent function: _setOption: function( key, value ) { ... return this._superApply( arguments ); } Finally we need to call _destroy to ensure our widget is reset back to the default, before either reinitializing it on screen or removing it completely: _destroy: function() { return this._super(); }, }); })(jQuery); Setting options in a widget Throughout this book, we've already seen that each of the different components in the library has a series of options that control widget features, the Widget Factory is no different. It has three base options you can use to configure the appearance of the widget; these are in addition to any that you add as custom options to your widget. [ 4 ] Chapter 15 The available options to configure behaviors are shown in the following table: Option Default value Use disabled false Disables the widget if set to true hide null Sets if or how to animate the hiding of the widget—it can accept boolean, string, number, or object as input show null Sets if or how to animate the displaying of the widget—it can accept boolean, string, number, or object as input Setting options on initialization In addition, we can add our own options to the widget, in a similar fashion to that you might use for creating jQuery plugins. It's good practice to expose the defaults for each option, so that users can change them when configuring your plugin. We can then merge these into the standard options that are exposed by the widget, before destroying them when we are finished with the widget. A common pattern in jQuery plugins looks like this: $.fn.plugin = function( options ) { options = $.extend( {}, $.fn.plugin.defaults, options ); // Plugin logic goes here. }; $.fn.plugin.defaults = { param1: "foo", param2: "bar", param3: "baz" }; The Widget Factory provides this functionality and takes it a bit further: $.widget( "al.plugin", { // Default options options: { param1: "test", param2: "foo", param3: "sample" }, _create: function() { [ 5 ] The Widget Factory // Options are already merged and stored in this.options // Plugin logic goes here. } }); Once we've set our options, we can then respond to changes in any of the options; we'll see how in the Watching out for changes section later in this chapter. Creating the base widget Now that we've set our initial options, we need to initiate our plugin. This is done using the _create() method. Here, we would call any methods that are needed to create our widget on screen and configure default values or settings for our widget: Option Default value Use _create() jQuery This is the widget's constructor method, which we use to initiate our widget. It doesn't require any parameters, but this.element and this.options are already set. Setting widget methods In a similar fashion to other components in the library, the Widget Factory exposes a number of other methods that we can use when creating our widgets, in addition to the core methods of destroy, disable, enable, option, and widget. Let's take a look at the some of the methods available, which are listed in the following table: Method Returns Use _delay Number Invokes the specified functionality after a specified delay. _destroy jQuery Cleans up all common data, events, and so on, and then delegates to _destroy() for custom, widget-specific, clean-up. _focusable jQuery Sets up the specified element to apply the ui-state-focus class on focus.