YUI 3.x Home -

Site Search (YDN & YUIBlog):

# YUI Library Examples: Widget: Creating a widget plugin

## Widget: Creating a widget plugin

This example shows how you can use Widget's plugin infrastructure to add additional features to an existing widget.

We create an IO plugin class for `Widget` called `WidgetIO`. The plugin adds IO capabilities to the Widget, which, by default, outputs to the `Widget`'s `contentBox`.

View example in new window.

### Creating an IO Plugin For Widget

#### Setting Up The YUI Instance

For this example, we'll pull in `widget`; the `io`, and the `plugin` module. The `io` module provides the XHR support we need for the IO plugin. The `Plugin` base class is the class we'll extend to create our io plugin class for `Widget`. The code to set up our sandbox instance is shown below:

    YUI({...}).use("widget", "io", "plugin", function(Y) {    // We'll write our code here, after pulling in the default Widget module,     // the IO utility, and the Plugin base class.}

    YUI({...}).use("widget", "io", "plugin", function(Y) {
        // We'll write our code here, after pulling in the default Widget module,
        // the IO utility, and the Plugin base class.
    }

YUI({...}).use("widget", "io", "plugin", function(Y) { // We'll write our code here, after pulling in the default Widget module, // the IO utility, and the Plugin base class. }

Using the `widget` module will also pull down the default CSS required for widget, on top of which we only need to add our required look/feel CSS for the example.

#### WidgetIO Class Structure

The `WidgetIO` class will extend the `Plugin` base class. Since `Plugin` derives from `Base`, we follow the same pattern we use for widgets and other utilities which extend Base to setup our new class.

Namely:

- Setting up the constructor to invoke the superclass constructor
- Setting up a `NAME` property, to identify the class
- Setting up the default attributes, using the `ATTRS` property
- Providing prototype implementations for anything we want executed during initialization and destruction using the `initializer` and `destructor` lifecycle methods

Additionally, since this is a plugin, we provide a `NS` property for the class, which defines the property which will refer to the `WidgetIO` instance on the host class (e.g. `widget.io` will be an instance of `WidgetIO`)

.

    /* Widget IO Plugin Constructor */function WidgetIO(config) {    WidgetIO.superclass.constructor.apply(this, arguments);} /*  * The namespace for the plugin. This will be the property on the widget,  * which will reference the plugin instance, when it's plugged in */WidgetIO.NS = "io"; /* * The NAME of the WidgetIO class. Used to prefix events generated * by the plugin class. */WidgetIO.NAME = "ioPlugin"; /* * The default set of attributes for the WidgetIO class. */WidgetIO.ATTRS = {    uri : {...},    cfg : {...},    formatter : {...},    loading: {...}}; /* Extend the base plugin class */Y.extend(WidgetIO, Y.Plugin.Base, {     // Lifecycle methods.    initializer: function() {...},     // IO Plugin specific methods    refresh : function() {...},     // Default IO transaction handlers    _defSuccessHandler : function(id, o) {...},    _defFailureHandler : function(id, o) {...},    _defStartHandler : function(id, o) {...},    _defCompleteHandler : function(id, o) {...},    _defFormatter : function(val) {...}});

    /* Widget IO Plugin Constructor */
    function WidgetIO(config) {
        WidgetIO.superclass.constructor.apply(this, arguments);
    }

    /*
     * The namespace for the plugin. This will be the property on the widget,
     * which will reference the plugin instance, when it's plugged in
     */
    WidgetIO.NS = "io";

    /*
     * The NAME of the WidgetIO class. Used to prefix events generated
     * by the plugin class.
     */
    WidgetIO.NAME = "ioPlugin";

    /*
     * The default set of attributes for the WidgetIO class.
     */
    WidgetIO.ATTRS = {
        uri : {...},
        cfg : {...},
        formatter : {...},
        loading: {...}
    };

    /* Extend the base plugin class */
    Y.extend(WidgetIO, Y.Plugin.Base, {

        // Lifecycle methods.
        initializer: function() {...},

        // IO Plugin specific methods
        refresh : function() {...},

        // Default IO transaction handlers
        _defSuccessHandler : function(id, o) {...},
        _defFailureHandler : function(id, o) {...},
        _defStartHandler : function(id, o) {...},
        _defCompleteHandler : function(id, o) {...},
        _defFormatter : function(val) {...}
    });

/* Widget IO Plugin Constructor */ function WidgetIO(config) { WidgetIO.superclass.constructor.apply(this, arguments); } /* * The namespace for the plugin. This will be the property on the widget, * which will reference the plugin instance, when it's plugged in */ WidgetIO.NS = "io"; /* * The NAME of the WidgetIO class. Used to prefix events generated * by the plugin class. */ WidgetIO.NAME = "ioPlugin"; /* * The default set of attributes for the WidgetIO class. */ WidgetIO.ATTRS = { uri : {...}, cfg : {...}, formatter : {...}, loading: {...} }; /* Extend the base plugin class */ Y.extend(WidgetIO, Y.Plugin.Base, { // Lifecycle methods. initializer: function() {...}, // IO Plugin specific methods refresh : function() {...}, // Default IO transaction handlers _defSuccessHandler : function(id, o) {...}, _defFailureHandler : function(id, o) {...}, _defStartHandler : function(id, o) {...}, _defCompleteHandler : function(id, o) {...}, _defFormatter : function(val) {...} });

#### Plugin Attributes

The `WidgetIO` is a fairly simple plugin class. It provides incremental functionality. It does not need to modify the behavior of any methods on the host Widget instance, or monitor any Widget events (unlike the AnimPlugin example).

It sets up the following attributes, which are used to control how the IO plugin's `refresh` method behaves:

uri
  The uri to use for the io request
cfg
  The io configuration object, to pass to io when initiating a transaction
formatter
  The formatter to use to formatting response data. The default implementation simply passes back the response data passed in, unchanged.
loading
  The default content to display while an io transaction is in progress

In terms of code, the attributes for the plugin are set up using the standard `ATTRS` property:

    /* Setup local variable for Y.WidgetStdMod, since we use it    multiple times to reference static properties */var WidgetIO = Y.WidgetIO; ... /* Attribute definition */WidgetIO.ATTRS = {    /*     * The node that will contain the IO content      */    contentNode: {        value: '.yui3-widget-content',        setter: '_defContentNodeSetter'    },     /*     * The uri to use for the io request     */    uri : {        value:null    },     /*     * The io configuration object, to pass to io when initiating      * a transaction     */    cfg : {        value:null    },     /*     * The default formatter to use when formatting response data. The default     * implementation simply passes back the response data passed in.      */    formatter : {        valueFn: function() {            return this._defFormatter;        }    },     /*     * The default loading indicator to use, when an io transaction is in progress.     */    loading: {        value: '<img class="yui3-loading" width="32px" \                     height="32px" src="assets/img/ajax-loader.gif">'    }};

    /* Setup local variable for Y.WidgetStdMod, since we use it
       multiple times to reference static properties */
    var WidgetIO = Y.WidgetIO;

    ...

    /* Attribute definition */
    WidgetIO.ATTRS = {
        /*
         * The node that will contain the IO content
         */
        contentNode: {
            value: '.yui3-widget-content',
            setter: '_defContentNodeSetter'
        },

        /*
         * The uri to use for the io request
         */
        uri : {
            value:null
        },

        /*
         * The io configuration object, to pass to io when initiating
         * a transaction
         */
        cfg : {
            value:null
        },

        /*
         * The default formatter to use when formatting response data. The default
         * implementation simply passes back the response data passed in.
         */
        formatter : {
            valueFn: function() {
                return this._defFormatter;
            }
        },

        /*
         * The default loading indicator to use, when an io transaction is in progress.
         */
        loading: {
            value: '<img class="yui3-loading" width="32px" \
                        height="32px" src="assets/img/ajax-loader.gif">'
        }
    };

/* Setup local variable for Y.WidgetStdMod, since we use it multiple times to reference static properties */ var WidgetIO = Y.WidgetIO; ... /* Attribute definition */ WidgetIO.ATTRS = { /* * The node that will contain the IO content */ contentNode: { value: '.yui3-widget-content', setter: '_defContentNodeSetter' }, /* * The uri to use for the io request */ uri : { value:null }, /* * The io configuration object, to pass to io when initiating * a transaction */ cfg : { value:null }, /* * The default formatter to use when formatting response data. The default * implementation simply passes back the response data passed in. */ formatter : { valueFn: function() { return this._defFormatter; } }, /* * The default loading indicator to use, when an io transaction is in progress. */ loading: { value: '<img class="yui3-loading" width="32px" \ height="32px" src="assets/img/ajax-loader.gif">' } };

The `formatter` attribute uses `valueFn` to define an instance based default value; pointing to the `_defFormatter` method on the `WidgetIO` instance.

#### Lifecycle Methods: initializer, destructor

Since no special initialization is required, there is no need to implement an `initializer` method.

The `destructor` terminates any existing transaction, if active when the plugin is destroyed (unplugged).

    initializer: function() {} /* * Destruction code. Terminates the activeIO transaction if it exists */destructor : function() {    if (this._activeIO) {        Y.io.abort(this._activeIO);        this._activeIO = null;    }},

    initializer: function() {}

    /*
     * Destruction code. Terminates the activeIO transaction if it exists
     */
    destructor : function() {
        if (this._activeIO) {
            Y.io.abort(this._activeIO);
            this._activeIO = null;
        }
    },

initializer: function() {} /* * Destruction code. Terminates the activeIO transaction if it exists */ destructor : function() { if (this._activeIO) { Y.io.abort(this._activeIO); this._activeIO = null; } },

#### The refresh Method

The `refresh` method is the main public method which the plugin provides. It's responsible for dispatching the IO request, using the current state of the attributes defined of the plugin. Users will end up invoking the method from the plugin instance attached to the Widget (`widget.io.refresh()`).

    refresh : function() {    if (!this._activeIO) {        var uri = this.get("uri");         if (uri) {            cfg = this.get("cfg") || {};            cfg.on = cfg.on || {};             cfg.on.start = cfg.on.start || Y.bind(this._defStartHandler, this);            cfg.on.complete = cfg.on.complete || Y.bind(this._defCompleteHandler, this);             cfg.on.success = cfg.on.success || Y.bind(this._defSuccessHandler, this);            cfg.on.failure = cfg.on.failure || Y.bind(this._defFailureHandler, this);             cfg.method = cfg.method; // io defaults to "GET" if not defined             Y.io(uri, cfg);        }    }}

    refresh : function() {
        if (!this._activeIO) {
            var uri = this.get("uri");

            if (uri) {
                cfg = this.get("cfg") || {};
                cfg.on = cfg.on || {};

                cfg.on.start = cfg.on.start || Y.bind(this._defStartHandler, this);
                cfg.on.complete = cfg.on.complete || Y.bind(this._defCompleteHandler, this);

                cfg.on.success = cfg.on.success || Y.bind(this._defSuccessHandler, this);
                cfg.on.failure = cfg.on.failure || Y.bind(this._defFailureHandler, this);

                cfg.method = cfg.method; // io defaults to "GET" if not defined

                Y.io(uri, cfg);
            }
        }
    }

refresh : function() { if (!this._activeIO) { var uri = this.get("uri"); if (uri) { cfg = this.get("cfg") || {}; cfg.on = cfg.on || {}; cfg.on.start = cfg.on.start || Y.bind(this._defStartHandler, this); cfg.on.complete = cfg.on.complete || Y.bind(this._defCompleteHandler, this); cfg.on.success = cfg.on.success || Y.bind(this._defSuccessHandler, this); cfg.on.failure = cfg.on.failure || Y.bind(this._defFailureHandler, this); cfg.method = cfg.method; // io defaults to "GET" if not defined Y.io(uri, cfg); } } }

The `refresh` method, as implemented for the scope of this example, sets up the io configuration object for the transaction it is about to dispatch, filling in the default handlers for io's `start`, `complete`, `success` and `failure` events, if the user does not provide custom implementations.

#### Node Content Helper Method

To simplify implementation and extension, a `setContent` method is used to wrap the content setter, which by default updates the `contentBox` of the widget.

    /* * Helper method for setting host content  */setContent: function(content) {    this.get('contentNode').setContent(content);}

    /*
     * Helper method for setting host content
     */
    setContent: function(content) {
        this.get('contentNode').setContent(content);
    }

/* * Helper method for setting host content */ setContent: function(content) { this.get('contentNode').setContent(content); }

#### The Default IO Event Handlers

The default success listener, pulls the response data from the response object, and uses it to update the content defined by the `contentNode` attribute, which, by default, is the `contentBox`. The response data is passed through the `formatter` configured for the plugin, converting it to the desired output format:

    _defSuccessHandler : function(id, o) {    var response = o.responseXML || o.responseText;    var formatter = this.get('formatter');     this.setContent(formatter(response));}

    _defSuccessHandler : function(id, o) {
        var response = o.responseXML || o.responseText;
        var formatter = this.get('formatter');

        this.setContent(formatter(response));
    }

_defSuccessHandler : function(id, o) { var response = o.responseXML || o.responseText; var formatter = this.get('formatter'); this.setContent(formatter(response)); }

The default failure listener, displays an error message in the currently configured `section`, when io communication fails:

    _defFailureHandler : function(id, o) {    this.setContent('Failed to retrieve content');}

    _defFailureHandler : function(id, o) {
        this.setContent('Failed to retrieve content');
    }

_defFailureHandler : function(id, o) { this.setContent('Failed to retrieve content'); }

The default start event listener renders the `loading` content, which remains in place while the transaction is in process, and also stores a reference to the "inprogress" io transaction:

    _defStartHandler : function(id, o) {    this._activeIO = o;    this.setContent(this.get('loading'));},

    _defStartHandler : function(id, o) {
        this._activeIO = o;
        this.setContent(this.get('loading'));
    },

_defStartHandler : function(id, o) { this._activeIO = o; this.setContent(this.get('loading')); },

The default complete event listener clears out the "inprogress" io transaction object:

    _defCompleteHandler : function(id, o) {    this._activeIO = null;}

    _defCompleteHandler : function(id, o) {
        this._activeIO = null;
    }

_defCompleteHandler : function(id, o) { this._activeIO = null; }

#### Using the Plugin

All objects derived from Base are Plugin Hosts. They provide `plug` and `unplug` methods to allow users to add/remove plugins to/from existing instances. They also allow the user to specify the set of plugins to be applied to a new instance, along with their configurations, as part of the constructor arguments.

In this example, we'll create a new instance of a Widget:

    /* Create a new Widget instance, with content generated from script */var widget = new Y.Widget();

    /* Create a new Widget instance, with content generated from script */
    var widget = new Y.Widget();

/* Create a new Widget instance, with content generated from script */ var widget = new Y.Widget();

And then use the `plug` method to add the `WidgetIO`, providing it with a configuration to use when sending out io transactions (The Animation Plugin example shows how you could do the same thing during construction), render the widget, and refresh the plugin to fetch the content.

    /* * Add the Plugin, and configure it to use a news feed uri  */widget.plug(WidgetIO, {    uri : 'assets/news.php?query=web+browser',    loading: '<img class="yui3-loading" width="32px" height="32px" src="assets/img/ajax-loader.gif">'    }); widget.render('#demo'); /* fetch the content */widget.io.refresh();

    /*
     * Add the Plugin, and configure it to use a news feed uri
     */
    widget.plug(WidgetIO, {
        uri : 'assets/news.php?query=web+browser',
        loading: '<img class="yui3-loading" width="32px" height="32px" src="assets/img/ajax-loader.gif">'
    });

    widget.render('#demo');

    /* fetch the content */
    widget.io.refresh();

/* * Add the Plugin, and configure it to use a news feed uri */ widget.plug(WidgetIO, { uri : 'assets/news.php?query=web+browser', loading: '<img class="yui3-loading" width="32px" height="32px" src="assets/img/ajax-loader.gif">' }); widget.render('#demo'); /* fetch the content */ widget.io.refresh();

The plugin class structure described above is captured in this "MyPlugin" Template File, which you can use as a starting point to create your own plugins derived from `Plugin.Base`.

Copyright © 2010 Yahoo! Inc. All rights reserved.

Privacy Policy - Terms of Service - Copyright Policy - Job Openings