From 153dec688d7b5ff709744779ba6c0e47153b09b8 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Mon, 16 Sep 2013 13:27:11 -0400 Subject: [PATCH 1/6] New article on extending widgets with the widget factory. --- .../widget-factory/extending-widgets.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 page/jquery-ui/widget-factory/extending-widgets.md diff --git a/page/jquery-ui/widget-factory/extending-widgets.md b/page/jquery-ui/widget-factory/extending-widgets.md new file mode 100644 index 00000000..6fd0ccd0 --- /dev/null +++ b/page/jquery-ui/widget-factory/extending-widgets.md @@ -0,0 +1,107 @@ +--- +title: Extending Widgets with the Widget Factory +level: intermediate +--- + +jQuery UI's widget factory makes it easy to build widgets that extend the functionality of existing widgets. Doing so allows you to build powerful widgets on top of an existing base, as well as make small tweaks to an existing widget's functionality. + +### Creating Widget Extensions + +Creating widgets with the widget factory is done by passing the name of the widget and a prototype object to `$.widget`. The following creats a "myWidget" widget in the "myNamespace" namespace. + +``` +$.widget( "myNamespace.myWidget" {} ); +``` + +`$.widget` also accepts the constructor of a widget to extend as an optional parameter. For instance, the constructor of [jQuery UI's dialog widget](http://jqueryui.com/dialog/) is `$.ui.dialog`. + +When specifying a base widget, pass it as a second argument - after the widget's name, and before the widget's prototype object. The following creates a "superDialog" widget based on the dialog widget in the `"my"` namespace. + +``` +$.widget( "my.superDialog", $.ui.dialog, {} ); +``` + +Here superDialog and dialog are essentially equivalent widgets with different names and namepaces. To make our new widget more interesting we can add methods to its prototype. + +The prototype object to use for a widget is the final argument passed to `$.widget`. To this point our examples have been using an empty object. Let's add a method to the prototype: + +``` +$.widget( "my.superDialog", $.ui.dialog, { + red: function() { + this.element.css( "color", "red" ); + } +}); + +// Create a new
, convert it into a superDialog, and call the red method. +$( "
I am red
" ) + .superDialog() + .superDialog( "red" ); +``` + +Now the `superDialog` has a `red()` method that will change its text red. Note how the widget factory automatically sets `this` equal to the widget's instance. For a full list of the methods and properties available on the instance see [the widget factory's API documentation](http://api.jqueryui.com/jquery.widget/). + +### Extending Existing Methods + +Sometimes you need to tweak or add to the behavior of existing widget methods. The do this, specify a method with the same name on the prototype object passed to `$.widget`. The following overrides dialog's [`open()` method](http://api.jqueryui.com/dialog/#method-open). Since dialogs automatically open by default, `"open"` will be logged when this code runs. + +``` +$.widget( "my.superDialog", $.ui.dialog, { + open: function() { + console.log( "open" ); + } +}); + +// Create a new
, and convert it into a superDialog +$( "
" ).superDialog(); +``` + +While this runs, there's a problem. Since we overrode the default behavior of `open()`, the dialog no longer displays on the screen. + +When we place methods on the prototype object we are not actually overriding the original method - rather, we are placing a new method at a higher level in the prototype chain. + +The original `show()` method is still available at `$.ui.dialog.prototype.open()`, and can be invoked. The following creates a new widget with an `open()` method that logs `"Opened"` then calls dialog's `open()`. + +``` +$.widget( "my.superDialog", $.ui.dialog, { + open: function() { + console.log( "Opened" ); + return $.ui.dialog.prototype.open.apply( this, arguments ); + } +}); + +$( "#dialog" ).superDialog(); +``` + +### Using _super to Access Parents + +Referencing the parent widget's prototype methods is a bit verbose, therefore in [jQuery UI 1.9](http://jqueryui.com/upgrade-guide/1.9/) the [`_super()` method](http://api.jqueryui.com/jquery.widget/#method-_super) was introduced. + +`_super()` invokes the method of the same name from the parent widget with the same set of arguments. Therefore we can replace our call to `$.ui.dialog.prototype.open()` with one to `_super()`. + +``` +$.widget( "my.superDialog", $.ui.dialog, { + open: function() { + console.log( "Opened" ); + return this._super(); + } +}); + +$( "#dialog" ).superDialog(); +``` + +### Redefining Widgets + +Another feature added in jQuery UI 1.9 was the ability for widgets to redefine themselves. Therefore, instead of creating a new widget we can pass `$.widget` an existing widget's name and constructor. The following example adds the same logging in `open()`, but doesn't create a new widget to do so. + +``` +$.widget( "ui.dialog", $.ui.dialog, { + open: function() { + console.log( "Opened" ); + return this._super(); + } +}); + +$( "#dialog" ).dialog(); +``` + +Now you can extend an existing widget's method, and still have access to the original methods using `_super()`. From 878403c3648be2284865d394e592d06ab6cf1e44 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Mon, 16 Sep 2013 15:42:32 -0400 Subject: [PATCH 2/6] Various improvements and tweaks per review and reread. --- .../widget-factory/extending-widgets.md | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/page/jquery-ui/widget-factory/extending-widgets.md b/page/jquery-ui/widget-factory/extending-widgets.md index 6fd0ccd0..4e22b3b1 100644 --- a/page/jquery-ui/widget-factory/extending-widgets.md +++ b/page/jquery-ui/widget-factory/extending-widgets.md @@ -7,15 +7,15 @@ jQuery UI's widget factory makes it easy to build widgets that extend the functi ### Creating Widget Extensions -Creating widgets with the widget factory is done by passing the name of the widget and a prototype object to `$.widget`. The following creats a "myWidget" widget in the "myNamespace" namespace. +Creating widgets with the widget factory is done by passing the name of the widget and a prototype object to `$.widget`. The following creates a "myWidget" widget in the "myNamespace" namespace. ``` -$.widget( "myNamespace.myWidget" {} ); +$.widget( "myNamespace.myWidget", {} ); ``` `$.widget` also accepts the constructor of a widget to extend as an optional parameter. For instance, the constructor of [jQuery UI's dialog widget](http://jqueryui.com/dialog/) is `$.ui.dialog`. -When specifying a base widget, pass it as a second argument - after the widget's name, and before the widget's prototype object. The following creates a "superDialog" widget based on the dialog widget in the `"my"` namespace. +When specifying a base widget, pass it as a second argument - after the widget's name, and before the widget's prototype object. The following creates a "superDialog" widget based on the dialog widget in the "my" namespace. ``` $.widget( "my.superDialog", $.ui.dialog, {} ); @@ -59,49 +59,72 @@ While this runs, there's a problem. Since we overrode the default behavior of `o When we place methods on the prototype object we are not actually overriding the original method - rather, we are placing a new method at a higher level in the prototype chain. -The original `show()` method is still available at `$.ui.dialog.prototype.open()`, and can be invoked. The following creates a new widget with an `open()` method that logs `"Opened"` then calls dialog's `open()`. +The original `show()` method is still available at `$.ui.dialog.prototype.open()`, and can be invoked. To make this easy, the widget factory provides two methods - `_super()` and `superApply()`. + +### Using `_super` and `_superApply` to Access Parents + +Referencing the parent widget's prototype methods is a bit verbose, therefore [jQuery UI 1.9](http://jqueryui.com/upgrade-guide/1.9/) introduced the [`_super()`](http://api.jqueryui.com/jquery.widget/#method-_super) and [`_superApply()`](http://api.jqueryui.com/jquery.widget/#method-_superApply) methods. + +`_super()` and `_superApply()` both invoke the method of the same name from the parent widget. For example the following creates a new widget with an `open()` method that logs `"open"`, then invokes dialog's `open()` method. ``` $.widget( "my.superDialog", $.ui.dialog, { open: function() { - console.log( "Opened" ); - return $.ui.dialog.prototype.open.apply( this, arguments ); + console.log( "open" ); + // Invoke the parent widget's open() + return this._super(); } }); -$( "#dialog" ).superDialog(); +$( "
" ).superDialog(); ``` -### Using _super to Access Parents - -Referencing the parent widget's prototype methods is a bit verbose, therefore in [jQuery UI 1.9](http://jqueryui.com/upgrade-guide/1.9/) the [`_super()` method](http://api.jqueryui.com/jquery.widget/#method-_super) was introduced. - -`_super()` invokes the method of the same name from the parent widget with the same set of arguments. Therefore we can replace our call to `$.ui.dialog.prototype.open()` with one to `_super()`. +`_super()` and `superApply()` were designed to behave like the native `Function.prototype.call()` and `Function.prototype.apply()` methods. Therefore, `_super()` accepts an argument list, and `superApply()` accepts a single array of arguments. This difference is shown in the example below. ``` $.widget( "my.superDialog", $.ui.dialog, { - open: function() { - console.log( "Opened" ); - return this._super(); + _setOption: function( key, value ) { + // Both invoke dialog's setOption method. _super requires the arguments be + // passed as an argument list, _superApply as a single array. + this._super( key, value ); + this._superApply( arguments ); } }); - -$( "#dialog" ).superDialog(); ``` ### Redefining Widgets -Another feature added in jQuery UI 1.9 was the ability for widgets to redefine themselves. Therefore, instead of creating a new widget we can pass `$.widget` an existing widget's name and constructor. The following example adds the same logging in `open()`, but doesn't create a new widget to do so. +Another feature added in jQuery UI 1.9 was the ability for widgets to redefine themselves. Therefore, instead of creating a new widget, we can pass `$.widget` an existing widget's name and constructor. The following example adds the same logging in `open()`, but doesn't create a new widget to do so. ``` $.widget( "ui.dialog", $.ui.dialog, { open: function() { - console.log( "Opened" ); + console.log( "open" ); return this._super(); } }); -$( "#dialog" ).dialog(); +$( "
" ).dialog(); ``` Now you can extend an existing widget's method, and still have access to the original methods using `_super()`. + +### Widgets and Polymorphism + +One word of warning when interacting with widget extensions and their plugins. The parent widget's plugin cannot be used to invoke methods on elements that are child widgets. This is shown in the example below. + +``` +$.widget( "my.superDialog", $.ui.dialog, {} ); + +var dialog = $( "
" ).superDialog(); + +// This works +dialog.superDialog( "close" ); + +// This doesn't +dialog.dialog( "close" ); +``` + +Above, the parent widget's plugin, `dialog()`, cannot invoke the `close()` method on an element that is a superDialog. If you need to access the parent's methods, you need to specify a new method on the prototype and use `_super()` or `_superApply()` to access the parent's method. + +For more on the invoking widget methods see [Widget Method Invocation](/jquery-ui/widget-factory/widget-method-invocation/). For more information on the widget plugin bridge see [its documentation](http://api.jqueryui.com/jQuery.widget.bridge/). From 83177abc0f1d2d52c4202bafc932c658d67ad317 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Thu, 19 Sep 2013 08:29:48 -0400 Subject: [PATCH 3/6] Various edits based on feedback and another re-read. --- .../widget-factory/extending-widgets.md | 108 ++++++++++++------ 1 file changed, 76 insertions(+), 32 deletions(-) diff --git a/page/jquery-ui/widget-factory/extending-widgets.md b/page/jquery-ui/widget-factory/extending-widgets.md index 4e22b3b1..91d4ccf3 100644 --- a/page/jquery-ui/widget-factory/extending-widgets.md +++ b/page/jquery-ui/widget-factory/extending-widgets.md @@ -1,77 +1,78 @@ --- title: Extending Widgets with the Widget Factory -level: intermediate +level: advanced --- jQuery UI's widget factory makes it easy to build widgets that extend the functionality of existing widgets. Doing so allows you to build powerful widgets on top of an existing base, as well as make small tweaks to an existing widget's functionality. +**Note:** This article assumes some basic knowledge of what the widget factory is and how it works. If you're unfamiliar with this, read up on [how to use the widget factory](/jquery-ui/widget-factory/how-to-use-the-widget-factory/) first. + ### Creating Widget Extensions -Creating widgets with the widget factory is done by passing the name of the widget and a prototype object to `$.widget`. The following creates a "myWidget" widget in the "myNamespace" namespace. +Creating widgets with the widget factory is done by passing the name of the widget and a prototype object to `$.widget()`. The following creates a "customWidget" widget in the "custom" namespace. ``` -$.widget( "myNamespace.myWidget", {} ); +$.widget( "custom.customWidget", {} ); ``` -`$.widget` also accepts the constructor of a widget to extend as an optional parameter. For instance, the constructor of [jQuery UI's dialog widget](http://jqueryui.com/dialog/) is `$.ui.dialog`. +To allow for extension, `$.widget()` optionally accepts the constructor of a widget to use as a parent. When specifying a parent widget, pass it as the second argument - after the widget's name, and before the widget's prototype object. -When specifying a base widget, pass it as a second argument - after the widget's name, and before the widget's prototype object. The following creates a "superDialog" widget based on the dialog widget in the "my" namespace. +Like the previous example, the following also creates a "superDialog" widget in the "custom" namespace. However, this time the constructor of [jQuery UI's dialog widget](http://jqueryui.com/dialog/) (`$.ui.dialog`) is passed, indicating that the superDialog widget should use jQuery UI's dialog widget as a parent. ``` -$.widget( "my.superDialog", $.ui.dialog, {} ); +$.widget( "custom.superDialog", $.ui.dialog, {} ); ``` -Here superDialog and dialog are essentially equivalent widgets with different names and namepaces. To make our new widget more interesting we can add methods to its prototype. +Here superDialog and dialog are essentially equivalent widgets with different names and namepaces. To make our new widget more interesting we can add methods to its prototype object. -The prototype object to use for a widget is the final argument passed to `$.widget`. To this point our examples have been using an empty object. Let's add a method to the prototype: +A widget's prototype object is the final argument passed to `$.widget()`. So far, our examples have been using an empty object. Let's add a method to this object: ``` -$.widget( "my.superDialog", $.ui.dialog, { +$.widget( "custom.superDialog", $.ui.dialog, { red: function() { this.element.css( "color", "red" ); } }); -// Create a new
, convert it into a superDialog, and call the red method. +// Create a new
, convert it into a superDialog, and call the red() method. $( "
I am red
" ) .superDialog() .superDialog( "red" ); ``` -Now the `superDialog` has a `red()` method that will change its text red. Note how the widget factory automatically sets `this` equal to the widget's instance. For a full list of the methods and properties available on the instance see [the widget factory's API documentation](http://api.jqueryui.com/jquery.widget/). +Now the `superDialog` has a `red()` method that will change the color of its text to red. Note how the widget factory automatically sets `this` to the widget's instance object. For a full list of the methods and properties available on the instance, see [the widget factory's API documentation](http://api.jqueryui.com/jquery.widget/). ### Extending Existing Methods -Sometimes you need to tweak or add to the behavior of existing widget methods. The do this, specify a method with the same name on the prototype object passed to `$.widget`. The following overrides dialog's [`open()` method](http://api.jqueryui.com/dialog/#method-open). Since dialogs automatically open by default, `"open"` will be logged when this code runs. +Sometimes you need to tweak or add to the behavior of existing widget methods. The do this, specify a method with the same name as the method you want to override on the prototype object. The following example overrides dialog's [`open()` method](http://api.jqueryui.com/dialog/#method-open). Since dialogs automatically open by default, `"open"` will be logged when this code runs. ``` -$.widget( "my.superDialog", $.ui.dialog, { +$.widget( "custom.superDialog", $.ui.dialog, { open: function() { console.log( "open" ); } }); -// Create a new
, and convert it into a superDialog +// Create a new
, and convert it into a superDialog. $( "
" ).superDialog(); ``` While this runs, there's a problem. Since we overrode the default behavior of `open()`, the dialog no longer displays on the screen. -When we place methods on the prototype object we are not actually overriding the original method - rather, we are placing a new method at a higher level in the prototype chain. - -The original `show()` method is still available at `$.ui.dialog.prototype.open()`, and can be invoked. To make this easy, the widget factory provides two methods - `_super()` and `superApply()`. +When we place methods on the prototype object, we are not actually overriding the original method - rather, we are placing a new method at a higher level in the prototype chain. -### Using `_super` and `_superApply` to Access Parents +To make the parent's methods available, the widget factory provides two methods - `_super()` and `superApply()`. -Referencing the parent widget's prototype methods is a bit verbose, therefore [jQuery UI 1.9](http://jqueryui.com/upgrade-guide/1.9/) introduced the [`_super()`](http://api.jqueryui.com/jquery.widget/#method-_super) and [`_superApply()`](http://api.jqueryui.com/jquery.widget/#method-_superApply) methods. +### Using `_super()` and `_superApply()` to Access Parents -`_super()` and `_superApply()` both invoke the method of the same name from the parent widget. For example the following creates a new widget with an `open()` method that logs `"open"`, then invokes dialog's `open()` method. +The [`_super()`](http://api.jqueryui.com/jquery.widget/#method-_super) and [`_superApply()`](http://api.jqueryui.com/jquery.widget/#method-_superApply) are methods that access methods of the same same in the parent widget. Refer to the following example. Like the previous one, this example also overrides the `open()` method to log `"open"`. However, this time `_super()` is run to invoke dialog's `open()` and open the dialog. ``` -$.widget( "my.superDialog", $.ui.dialog, { +$.widget( "custom.superDialog", $.ui.dialog, { open: function() { console.log( "open" ); - // Invoke the parent widget's open() + + // Invoke the parent widget's open(). return this._super(); } }); @@ -79,13 +80,14 @@ $.widget( "my.superDialog", $.ui.dialog, { $( "
" ).superDialog(); ``` -`_super()` and `superApply()` were designed to behave like the native `Function.prototype.call()` and `Function.prototype.apply()` methods. Therefore, `_super()` accepts an argument list, and `superApply()` accepts a single array of arguments. This difference is shown in the example below. +`_super()` and `superApply()` were designed to behave like the native `Function.prototype.call()` and `Function.prototype.apply()` methods. Therefore, `_super()` accepts an argument list, and `_superApply()` accepts a single array of arguments. This difference is shown in the example below. ``` -$.widget( "my.superDialog", $.ui.dialog, { +$.widget( "custom.superDialog", $.ui.dialog, { _setOption: function( key, value ) { - // Both invoke dialog's setOption method. _super requires the arguments be - // passed as an argument list, _superApply as a single array. + + // Both invoke dialog's setOption() method. _super() requires the arguments + // be passed as an argument list, _superApply() as a single array. this._super( key, value ); this._superApply( arguments ); } @@ -94,7 +96,7 @@ $.widget( "my.superDialog", $.ui.dialog, { ### Redefining Widgets -Another feature added in jQuery UI 1.9 was the ability for widgets to redefine themselves. Therefore, instead of creating a new widget, we can pass `$.widget` an existing widget's name and constructor. The following example adds the same logging in `open()`, but doesn't create a new widget to do so. +jQuery UI 1.9 added the ability for widgets to redefine themselves. Therefore, instead of creating a new widget, we can pass `$.widget()` an existing widget's name and constructor. The following example adds the same logging in `open()`, but doesn't create a new widget to do so. ``` $.widget( "ui.dialog", $.ui.dialog, { @@ -107,24 +109,66 @@ $.widget( "ui.dialog", $.ui.dialog, { $( "
" ).dialog(); ``` -Now you can extend an existing widget's method, and still have access to the original methods using `_super()`. +With this approach you can extend an existing widget's method and still have access to the original methods using `_super()` - all without creating a new widget. ### Widgets and Polymorphism One word of warning when interacting with widget extensions and their plugins. The parent widget's plugin cannot be used to invoke methods on elements that are child widgets. This is shown in the example below. ``` -$.widget( "my.superDialog", $.ui.dialog, {} ); +$.widget( "custom.superDialog", $.ui.dialog, {} ); var dialog = $( "
" ).superDialog(); -// This works +// This works. dialog.superDialog( "close" ); -// This doesn't +// This doesn't. dialog.dialog( "close" ); ``` -Above, the parent widget's plugin, `dialog()`, cannot invoke the `close()` method on an element that is a superDialog. If you need to access the parent's methods, you need to specify a new method on the prototype and use `_super()` or `_superApply()` to access the parent's method. +Above, the parent widget's plugin, `dialog()`, cannot invoke the `close()` method on an element that is a superDialog. If you need to access the parent's methods, you need to specify a new method on the widget's prototype object and use `_super()` or `_superApply()` to access the parent's method. For more on the invoking widget methods see [Widget Method Invocation](/jquery-ui/widget-factory/widget-method-invocation/). For more information on the widget plugin bridge see [its documentation](http://api.jqueryui.com/jQuery.widget.bridge/). + +### Customizing Individual Instances + +All the examples we have looked at so far have extended methods on the widget's prototype. Methods overridden on the prototype affect all instances of the widget. + +To show this refer to the example below; both instances of the dialog use the same `open()` method. + +``` +$.widget( "ui.dialog", $.ui.dialog, { + open: function() { + console.log( "open" ); + return this._super(); + } +}); + +// Create two dialogs, both use the same open(), therefore "open" is logged twice. +$( "
" ).dialog(); +$( "
" ).dialog(); +``` + +While this is powerful, sometimes you only need to change the behavior for a single instance of the widget. To do this, obtain a reference to the instance and override the method using normal JavaScript property assignment. The example below shows this. + +``` +var dialogInstance = $( "
" ) + .dialog() + // Retrieve the dialog's instance and store it. + .data( "ui-dialog" ); + +// Override the close() method for this dialog +dialogInstance.close = function() { + console.log( "close" ); +}; + +// Create a second dialog +$( "
" ).dialog(); + +// Select both dialogs and call close() on each of them. +// "close" will only be logged once. +$( ":data(ui-dialog)" ).dialog( "close" ); +``` + +This technique of overriding methods for individual instances is perfect for one-off customizations. From 7435684a347eab9bc818ce7f47984560d1821852 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 20 Sep 2013 11:30:09 -0400 Subject: [PATCH 4/6] Link to the extending widgets article and various style updates. --- .../how-to-use-the-widget-factory.md | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/page/jquery-ui/widget-factory/how-to-use-the-widget-factory.md b/page/jquery-ui/widget-factory/how-to-use-the-widget-factory.md index 94acfec0..fe3b175f 100644 --- a/page/jquery-ui/widget-factory/how-to-use-the-widget-factory.md +++ b/page/jquery-ui/widget-factory/how-to-use-the-widget-factory.md @@ -3,7 +3,7 @@ title: How To Use the Widget Factory level: Beginner --- -To start, we'll create a progress bar that just lets us set the progress once. As we can see below, this is done by calling `jQuery.widget` with two parameters: the name of the plugin to create, and an object literal containing functions to support our plugin. When our plugin gets called, it will create a new plugin instance and all functions will be executed within the context of that instance. This is different from a standard jQuery plugin in two important ways. First, the context is an object, not a DOM element. Second, the context is always a single object, never a collection. +To start, we'll create a progress bar that just lets us set the progress once. As we can see below, this is done by calling `jQuery.widget()` with two parameters: the name of the plugin to create, and an object literal containing functions to support our plugin. When our plugin gets called, it will create a new plugin instance and all functions will be executed within the context of that instance. This is different from a standard jQuery plugin in two important ways. First, the context is an object, not a DOM element. Second, the context is always a single object, never a collection. ``` $.widget( "custom.progressbar", { @@ -16,7 +16,9 @@ $.widget( "custom.progressbar", { }); ``` -The name of the plugin must contain a namespace, in this case we've used the `custom` namespace. There is currently a limitation that exactly one namespace must be used. We can also see that the widget factory has provided two properties for us. `this.element` is a jQuery object containing exactly one element. If our plugin is called on a jQuery object containing multiple elements, a separate plugin instance will be created for each element, and each instance will have its own `this.element`. The second property, `this.options`, is a hash containing key/value pairs for all of our plugin's options. These options can be passed to our plugin as shown here. +The name of the plugin must contain a namespace, in this case we've used the `custom` namespace. You can only create namespaces that are one level deep, therefore, `custom.progressbar` is a valid plugin name whereas `very.custom.progressbar` is not. + +We can also see that the widget factory has provided two properties for us. `this.element` is a jQuery object containing exactly one element. If our plugin is called on a jQuery object containing multiple elements, a separate plugin instance will be created for each element, and each instance will have its own `this.element`. The second property, `this.options`, is a hash containing key/value pairs for all of our plugin's options. These options can be passed to our plugin as shown here. ``` $( "
" ) @@ -24,10 +26,11 @@ $( "
" ) .progressbar({ value: 20 }); ``` -When we call `jQuery.widget` it extends jQuery by adding a function to `jQuery.fn` (the system for creating a standard plugin). The name of the function it adds is based on the name you pass to `jQuery.widget`, without the namespace; in our case "progressbar". The options passed to our plugin are the values that get set in `this.options` inside of our plugin instance. As shown below, we can specify default values for any of our options. When designing your API, you should figure out the most common use case for your plugin so that you can set appropriate default values and make all options truly optional. +When we call `jQuery.widget()` it extends jQuery by adding a function to `jQuery.fn` (the system for creating a standard plugin). The name of the function it adds is based on the name you pass to `jQuery.widget()`, without the namespace - in our case "progressbar". The options passed to our plugin are the values that get set in `this.options` inside of our plugin instance. As shown below, we can specify default values for any of our options. When designing your API, you should figure out the most common use case for your plugin so that you can set appropriate default values and make all options truly optional. ``` $.widget( "custom.progressbar", { + // Default options. options: { value: 0 @@ -43,7 +46,7 @@ $.widget( "custom.progressbar", { ### Calling Plugin Methods -Now that we can initialize our progress bar, we'll add the ability to perform actions by calling methods on our plugin instance. To define a plugin method, we just include the function in the object literal that we pass to `jQuery.widget`. We can also define "private" methods by prepending an underscore to the function name. +Now that we can initialize our progress bar, we'll add the ability to perform actions by calling methods on our plugin instance. To define a plugin method, we just include the function in the object literal that we pass to `jQuery.widget()`. We can also define "private" methods by prepending an underscore to the function name. ``` $.widget( "custom.progressbar", { @@ -61,6 +64,7 @@ $.widget( "custom.progressbar", { // Create a public method. value: function( value ) { + // No value passed, act as a getter. if ( value === undefined ) { return this.options.value; @@ -106,7 +110,7 @@ alert( bar.progressbar( "value" ) ); ### Working with Options -One of the methods that are automatically available to our plugin is the `option` method. The `option` method allows you to get and set options after initialization. This method works exactly like jQuery's `.css()` and `.attr()` methods: You can pass just a name to use it as a getter, a name and value to use it as a single setter, or a hash of name/value pairs to set multiple values. When used as a getter, the plugin will return the current value of the option that corresponds to the name that was passed in. When used as a setter, the plugin's `_setOption` method will be called for each option that is being set. We can specify a `_setOption` method in our plugin to react to option changes. For actions to perform independent of the number of options changed, we can override `_setOptions`. +One of the methods that are automatically available to our plugin is the `option()` method. The `option()` method allows you to get and set options after initialization. This method works exactly like jQuery's `.css()` and `.attr()` methods: You can pass just a name to use it as a getter, a name and value to use it as a single setter, or a hash of name/value pairs to set multiple values. When used as a getter, the plugin will return the current value of the option that corresponds to the name that was passed in. When used as a setter, the plugin's `_setOption` method will be called for each option that is being set. We can specify a `_setOption` method in our plugin to react to option changes. For actions to perform independent of the number of options changed, we can override `_setOptions`. ``` $.widget( "custom.progressbar", { @@ -145,7 +149,7 @@ $.widget( "custom.progressbar", { ### Adding Callbacks -One of the easiest ways to make your plugin extensible is to add callbacks so users can react when the state of your plugin changes. We can see below how to add a callback to our progress bar to signify when the progress has reached 100%. The `_trigger` method takes three parameters: the name of the callback, a jQuery event object that initiated the callback, and a hash of data relevant to the event. The callback name is the only required parameter, but the others can be very useful for users who want to implement custom functionality on top of your plugin. For example, if we were building a draggable plugin, we could pass the mousemove event when triggering a drag callback; this would allow users to react to the drag based on the x/y coordinates provided by the event object. Note that the original event passed to `_trigger` must be a jQuery event, not a native browser event. +One of the easiest ways to make your plugin extensible is to add callbacks so users can react when the state of your plugin changes. We can see below how to add a callback to our progress bar to signify when the progress has reached 100%. The `_trigger()` method takes three parameters: the name of the callback, a jQuery event object that initiated the callback, and a hash of data relevant to the event. The callback name is the only required parameter, but the others can be very useful for users who want to implement custom functionality on top of your plugin. For example, if we were building a draggable plugin, we could pass the mousemove event when triggering a drag callback; this would allow users to react to the drag based on the x/y coordinates provided by the event object. Note that the original event passed to `_trigger()` must be a jQuery event, not a native browser event. ``` $.widget( "custom.progressbar", { @@ -185,7 +189,7 @@ $.widget( "custom.progressbar", { }); ``` -Callback functions are essentially just additional options, so you can get and set them just like any other option. Whenever a callback is executed, a corresponding event is triggered as well. The event type is determined by concatenating the plugin name and the callback name. The callback and event both receive the same two parameters: an event object and a hash of data relevant to the event, as we'll see below. Your plugin may have functionality that you want to allow the user to prevent. The best way to support this is by creating cancelable callbacks. Users can cancel a callback, or its associated event, the same way they cancel any native event, by calling `event.preventDefault()` or returning `false`. If the user cancels the callback, the `_trigger` method will return `false` so you can implement the appropriate functionality within your plugin. +Callback functions are essentially just additional options, so you can get and set them just like any other option. Whenever a callback is executed, a corresponding event is triggered as well. The event type is determined by concatenating the plugin name and the callback name. The callback and event both receive the same two parameters: an event object and a hash of data relevant to the event, as we'll see below. Your plugin may have functionality that you want to allow the user to prevent. The best way to support this is by creating cancelable callbacks. Users can cancel a callback, or its associated event, the same way they cancel any native event, by calling `event.preventDefault()` or returning `false`. If the user cancels the callback, the `_trigger()` method will return `false` so you can implement the appropriate functionality within your plugin. ``` var bar = $( "
" ) @@ -205,7 +209,7 @@ bar.progressbar( "option", "value", 100 ); ## Looking Under the Hood -Now that we've seen how to build a plugin using the widget factory, let's take a look at how it actually works. When you call `jQuery.widget`, it creates a constructor for your plugin and sets the object literal that you pass in as the prototype for your plugin instances. All of the functionality that automatically gets added to your plugin comes from a base widget prototype, which is defined as `jQuery.Widget.prototype`. When a plugin instance is created, it is stored on the original DOM element using `jQuery.data`, with the plugin name as the key. +Now that we've seen how to build a plugin using the widget factory, let's take a look at how it actually works. When you call `jQuery.widget()`, it creates a constructor for your plugin and sets the object literal that you pass in as the prototype for your plugin instances. All of the functionality that automatically gets added to your plugin comes from a base widget prototype, which is defined as `jQuery.Widget.prototype`. When a plugin instance is created, it is stored on the original DOM element using `jQuery.data`, with the plugin name as the key. Because the plugin instance is directly linked to the DOM element, you can access the plugin instance directly instead of going through the exposed plugin method if you want. This will allow you to call methods directly on the plugin instance instead of passing method names as strings and will also give you direct access to the plugin's properties. @@ -241,9 +245,11 @@ $.custom.progressbar.prototype.reset = function() { }; ``` +For more information on extending widgets, including how to build entirely new widgets on top of existing ones, see [Extending Widgets with the Widget Factory](/jquery-ui/widget-factory/extending-widgets/). + ### Cleaning Up -In some cases, it will make sense to allow users to apply and then later unapply your plugin. You can accomplish this via the `_destroy` method. Within the `_destroy` method, you should undo anything your plugin may have done during initialization or later use. `_destroy` is called by the `.destroy()` method, which is automatically called if the element that your plugin instance is tied to is removed from the DOM, so this can be used for garbage collection as well. That base `.destroy()` method also handles some general cleanup operations, like removing the instance reference from the widget's DOM element, unbinding all events in the widget's namespace from the element, and unbinding generally all events that were added using `_bind`. +In some cases, it will make sense to allow users to apply and then later unapply your plugin. You can accomplish this via the `_destroy()` method. Within the `_destroy()` method, you should undo anything your plugin may have done during initialization or later use. `_destroy()` is called by the `.destroy()` method, which is automatically called if the element that your plugin instance is tied to is removed from the DOM, so this can be used for garbage collection as well. That base `.destroy()` method also handles some general cleanup operations, like removing the instance reference from the widget's DOM element, unbinding all events in the widget's namespace from the element, and unbinding generally all events that were added using `_bind()`. ``` $.widget( "custom.progressbar", { From e1df9477dce0a6fb0ed47eec5bc201e6421c5be4 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 20 Sep 2013 12:38:15 -0400 Subject: [PATCH 5/6] Naming consistency. --- page/jquery-ui/widget-factory/extending-widgets.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/page/jquery-ui/widget-factory/extending-widgets.md b/page/jquery-ui/widget-factory/extending-widgets.md index 91d4ccf3..0f3fa539 100644 --- a/page/jquery-ui/widget-factory/extending-widgets.md +++ b/page/jquery-ui/widget-factory/extending-widgets.md @@ -9,10 +9,10 @@ jQuery UI's widget factory makes it easy to build widgets that extend the functi ### Creating Widget Extensions -Creating widgets with the widget factory is done by passing the name of the widget and a prototype object to `$.widget()`. The following creates a "customWidget" widget in the "custom" namespace. +Creating widgets with the widget factory is done by passing the name of the widget and a prototype object to `$.widget()`. The following creates a "superDialog" widget in the "custom" namespace. ``` -$.widget( "custom.customWidget", {} ); +$.widget( "custom.superDialog", {} ); ``` To allow for extension, `$.widget()` optionally accepts the constructor of a widget to use as a parent. When specifying a parent widget, pass it as the second argument - after the widget's name, and before the widget's prototype object. From ae7f758ce9df8137b3a23ffc1aed31bb253dfde4 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 20 Sep 2013 15:23:16 -0400 Subject: [PATCH 6/6] Minor clarifications. --- page/jquery-ui/widget-factory/extending-widgets.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/page/jquery-ui/widget-factory/extending-widgets.md b/page/jquery-ui/widget-factory/extending-widgets.md index 0f3fa539..c5dc8e21 100644 --- a/page/jquery-ui/widget-factory/extending-widgets.md +++ b/page/jquery-ui/widget-factory/extending-widgets.md @@ -61,11 +61,11 @@ While this runs, there's a problem. Since we overrode the default behavior of `o When we place methods on the prototype object, we are not actually overriding the original method - rather, we are placing a new method at a higher level in the prototype chain. -To make the parent's methods available, the widget factory provides two methods - `_super()` and `superApply()`. +To make the parent's methods available, the widget factory provides two methods - `_super()` and `_superApply()`. ### Using `_super()` and `_superApply()` to Access Parents -The [`_super()`](http://api.jqueryui.com/jquery.widget/#method-_super) and [`_superApply()`](http://api.jqueryui.com/jquery.widget/#method-_superApply) are methods that access methods of the same same in the parent widget. Refer to the following example. Like the previous one, this example also overrides the `open()` method to log `"open"`. However, this time `_super()` is run to invoke dialog's `open()` and open the dialog. +[`_super()`](http://api.jqueryui.com/jquery.widget/#method-_super) and [`_superApply()`](http://api.jqueryui.com/jquery.widget/#method-_superApply) invoke methods of the same same in the parent widget. Refer to the following example. Like the previous one, this example also overrides the `open()` method to log `"open"`. However, this time `_super()` is run to invoke dialog's `open()` and open the dialog. ``` $.widget( "custom.superDialog", $.ui.dialog, { @@ -80,7 +80,7 @@ $.widget( "custom.superDialog", $.ui.dialog, { $( "
" ).superDialog(); ``` -`_super()` and `superApply()` were designed to behave like the native `Function.prototype.call()` and `Function.prototype.apply()` methods. Therefore, `_super()` accepts an argument list, and `_superApply()` accepts a single array of arguments. This difference is shown in the example below. +`_super()` and `_superApply()` were designed to behave like the native `Function.prototype.call()` and `Function.prototype.apply()` methods. Therefore, `_super()` accepts an argument list, and `_superApply()` accepts a single array of arguments. This difference is shown in the example below. ``` $.widget( "custom.superDialog", $.ui.dialog, { @@ -127,15 +127,13 @@ dialog.superDialog( "close" ); dialog.dialog( "close" ); ``` -Above, the parent widget's plugin, `dialog()`, cannot invoke the `close()` method on an element that is a superDialog. If you need to access the parent's methods, you need to specify a new method on the widget's prototype object and use `_super()` or `_superApply()` to access the parent's method. - -For more on the invoking widget methods see [Widget Method Invocation](/jquery-ui/widget-factory/widget-method-invocation/). For more information on the widget plugin bridge see [its documentation](http://api.jqueryui.com/jQuery.widget.bridge/). +Above, the parent widget's plugin, `dialog()`, cannot invoke the `close()` method on an element that is a superDialog. For more on the invoking widget methods see [Widget Method Invocation](/jquery-ui/widget-factory/widget-method-invocation/). ### Customizing Individual Instances All the examples we have looked at so far have extended methods on the widget's prototype. Methods overridden on the prototype affect all instances of the widget. -To show this refer to the example below; both instances of the dialog use the same `open()` method. +To show this, refer to the example below; both instances of the dialog use the same `open()` method. ``` $.widget( "ui.dialog", $.ui.dialog, {