Skip to content

Commit 13bee11

Browse files
committed
Widget Factory: preserve changes and redirect
Follows-up a2a01a4. * Before the merging of these two pages, the two were not exaclty identical. History: https://github.com/jquery/learn.jquery.com/commits/aefe20bae4/page/plugins/stateful-plugins-with-widget-factory.md including: - Fix rendering negative progressbar value 48242d8 - "native event" -> "jQuery event" 4fda0f1 * Maintain a redirect, especially since it appears that the page we removed was the more popular one in terms of search ranking. Ref #636
1 parent 5833d53 commit 13bee11

File tree

4 files changed

+57
-17
lines changed

4 files changed

+57
-17
lines changed

Gruntfile.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,6 @@ jqueryContent.postPreprocessors.page = (function() {
5656

5757
grunt.registerTask( "build", [ "build-posts", "build-resources" ] );
5858

59+
grunt.registerTask( "deploy", [ "wordpress-deploy", "deploy-redirects" ] );
60+
5961
};

page/jquery-ui/widget-factory/how-to-use-the-widget-factory.md

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
<script>{
22
"title": "How To Use the Widget Factory",
3-
"level": "beginner"
3+
"level": "beginner",
4+
"source": "http://jqfundamentals.com/legacy",
5+
"attribution": [
6+
"jQuery Fundamentals",
7+
"Scott González <[email protected]> (http://blog.nemikor.com/2010/05/15/building-stateful-jquery-plugins/)"
8+
]
49
}</script>
510

6-
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.
11+
While most existing jQuery plugins are stateless – that is, we call them on an element and that is the extent of our interaction with the plugin – there's a large set of functionality that doesn't fit into the basic plugin pattern.
12+
13+
In order to 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 plugin, and provides various extension points. This system is called the Widget Factory and is exposed as `jQuery.widget` as part of jQuery UI 1.8; however, it can be used independently of jQuery UI.
14+
15+
To demonstrate the capabilities of the Widget Factory, we'll build a simple progress bar plugin.
16+
17+
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.
18+
19+
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.
20+
21+
A simple, stateful plugin using the jQuery UI Widget Factory:
722

823
```
924
$.widget( "custom.progressbar", {
@@ -18,15 +33,23 @@ $.widget( "custom.progressbar", {
1833

1934
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.
2035

36+
*Note:* In our example we use the `custom` namespace. The `ui` namespace is reserved for official jQuery UI plugins. When building your own plugins, you should create your own namespace. This makes it clear where the plugin came from and whether it is part of a larger collection.
37+
2138
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.
2239

40+
Passing options to a widget:
41+
2342
```
2443
$( "<div></div>" )
2544
.appendTo( "body" )
2645
.progressbar({ value: 20 });
2746
```
2847

29-
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.
48+
When we call `jQuery.widget()` it extends jQuery by adding a method to `jQuery.fn` (the same way we'd create 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 it will create `jQuery.fn.progressbar`. The options passed to our plugin are the values that get set in `this.options` inside of our plugin instance.
49+
50+
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.
51+
52+
Setting default options for a widget:
3053

3154
```
3255
$.widget( "custom.progressbar", {
@@ -44,17 +67,17 @@ $.widget( "custom.progressbar", {
4467
});
4568
```
4669

47-
### Calling Plugin Methods
70+
### Adding Methods to a Widget
4871

4972
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.
5073

74+
Creating widget methods:
75+
5176
```
5277
$.widget( "custom.progressbar", {
53-
5478
options: {
5579
value: 0
5680
},
57-
5881
_create: function() {
5982
var progress = this.options.value + "%";
6083
this.element
@@ -93,6 +116,8 @@ To call a method on a plugin instance, you pass the name of the method to the jQ
93116

94117
**Note:** Executing methods by passing the method name to the same jQuery function that was used to initialize the plugin may seem odd. This is done to prevent pollution of the jQuery namespace while maintaining the ability to chain method calls. Later in this article we'll see alternative uses that may feel more natural.
95118

119+
Calling methods on a plugin instance:
120+
96121
```
97122
var bar = $( "<div></div>" )
98123
.appendTo( "body" )
@@ -112,6 +137,8 @@ alert( bar.progressbar( "value" ) );
112137

113138
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()`.
114139

140+
Responding when an option is set:
141+
115142
```
116143
$.widget( "custom.progressbar", {
117144
options: {
@@ -150,7 +177,11 @@ $.widget( "custom.progressbar", {
150177

151178
### Adding Callbacks
152179

153-
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.
180+
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.
181+
182+
Note that the event passed to `_trigger()` must be a jQuery event, not a native browser event.
183+
184+
Providing callbacks for user extension:
154185

155186
```
156187
$.widget( "custom.progressbar", {
@@ -191,7 +222,11 @@ $.widget( "custom.progressbar", {
191222
});
192223
```
193224

194-
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.
225+
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.
226+
227+
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 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.
228+
229+
Binding to widget events:
195230

196231
```
197232
var bar = $( "<div></div>" )
@@ -211,7 +246,11 @@ bar.progressbar( "option", "value", 100 );
211246

212247
## Looking Under the Hood
213248

214-
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.
249+
Now that we've seen how to build a plugin using the widget factory, let's take a look at how it actually works.
250+
251+
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's full name (the plugin's namespace, plus a hyphen, plus the plugin's name) as the key.
252+
253+
For example the jQuery UI dialog widget uses a key of `"ui-dialog"`.
215254

216255
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.
217256

@@ -253,6 +292,8 @@ For more information on extending widgets, including how to build entirely new w
253292

254293
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()`.
255294

295+
Adding a destroy method to a widget:
296+
256297
```
257298
$.widget( "custom.progressbar", {
258299
options: {
@@ -299,6 +340,4 @@ $.widget( "custom.progressbar", {
299340

300341
## Closing Comments
301342

302-
The widget factory is only one way of creating stateful plugins. There are a few different models that can be used and each has their own advantages and disadvantages. The widget factory solves lots of common problems for you and can greatly improve productivity, it also greatly improves code reuse, making it a great fit for jQuery UI as well as many other stateful plugins.
303-
304-
You may have noticed that in this article we used the `custom` namespace. The `ui` namespace is reserved for official jQuery UI plugins. When building your own plugins, you should create your own namespace. This makes it clear where the plugin came from and if it is part of a larger collection.
343+
The Widget Factory is only one way of creating stateful plugins. There are a few different models that can be used and each has their own advantages and disadvantages. The Widget Factory solves lots of common problems for you and can greatly improve productivity, it also greatly improves code reuse, making it a great fit for jQuery UI as well as many other stateful plugins.

page/plugins.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,4 @@ The idea of a plugin is to do something with a collection of elements. You could
1515

1616
You can make your own plugins and use them privately in your code or you can release them into the wild. There are thousands of jQuery plugins available online. The barrier to creating a plugin of your own is so low that you'll want to do it straight away!
1717

18-
While most existing jQuery plugins are stateless – that is, we call them on an element and that is the extent of our interaction with the plugin – there's a large set of functionality that doesn't fit into the basic plugin pattern.
19-
20-
In order to 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 plugin, and provides various extension points. This system is called the Widget Factory and is exposed as `jQuery.widget` as part of jQuery UI 1.8; however, it can be used independently of jQuery UI.
21-
22-
For details on the capabilities of the Widget Factory, see this [How To Use the Widget Factory](/jquery-ui/widget-factory/how-to-use-the-widget-factory/).
18+
For details on how to create jQuery UI plugins via the Widget Factory, refer to [How To Use the Widget Factory](/jquery-ui/widget-factory/how-to-use-the-widget-factory/).

redirects.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"/plugins/stateful-plugins-with-widget-factory/": "/jquery-ui/widget-factory/how-to-use-the-widget-factory/"
3+
}

0 commit comments

Comments
 (0)