getting the most out of jquery widgets

51
Richard Lindsey @Velveeta http://conqueringtheclient.com/ PLATFORM FEE ARCHITECT | THE ADVISORY BOARD COMPANY jQuery Widgets GETTING THE MOST OUT OF

Upload: velveeta512

Post on 19-May-2015

2.096 views

Category:

Technology


0 download

DESCRIPTION

Richard Lindsey's presentation from the 2013 jQuery Conference in Austin, Tx.

TRANSCRIPT

Page 1: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/PLATFORM FEE ARCHITECT | THE ADVISORY BOARD COMPANY

jQuery Widgets

GETTING THE MOST OUT OF

Page 2: Getting the Most Out of jQuery Widgets

Let’s say we’re making Widgets…

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 3: Getting the Most Out of jQuery Widgets

What’s a Widget?

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 4: Getting the Most Out of jQuery Widgets

ELEMENTS / COMPOUNDS /CELLS / ORGANISMS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Thinksmall.Thinkmodular.

Page 5: Getting the Most Out of jQuery Widgets

Communicate through

events.KEEP COMPONENTS DECOUPLED / MAKE THEM SUBSCRIBE AND RESPOND

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 6: Getting the Most Out of jQuery Widgets

Communicate through

events.KEEP COMPONENTS DECOUPLED / MAKE THEM SUBSCRIBE AND RESPOND

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 7: Getting the Most Out of jQuery Widgets

Observe and

mediate.BUNDLE SMALLER MODULES / PROVIDE PUBLIC API / DIRECT REFERENCES SHOULD ONLY GO DOWNWARDS / EACH LAYER CONSUMES LOWER-LEVEL EVENTS & PUBLISHES UPWARDS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 8: Getting the Most Out of jQuery Widgets

Observe and

mediate.BUNDLE SMALLER MODULES / PROVIDE PUBLIC API / DIRECT REFERENCES SHOULD ONLY GO DOWNWARDS / EACH LAYER CONSUMES LOWER-LEVEL EVENTS & PUBLISHES UPWARDS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 9: Getting the Most Out of jQuery Widgets

Observe and

mediate.BUNDLE SMALLER MODULES / PROVIDE PUBLIC API / DIRECT REFERENCES SHOULD ONLY GO DOWNWARDS / EACH LAYER CONSUMES LOWER-LEVEL EVENTS & PUBLISHES UPWARDS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 10: Getting the Most Out of jQuery Widgets

Observe and

mediate.BUNDLE SMALLER MODULES / PROVIDE PUBLIC API / DIRECT REFERENCES SHOULD ONLY GO DOWNWARDS / EACH LAYER CONSUMES LOWER-LEVEL EVENTS & PUBLISHES UPWARDS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 11: Getting the Most Out of jQuery Widgets

$.widget(‘abc.autocomplete’, {_create: function () {

this._widgets = {dataloader: {loader:{}},optionlist: {results:{}},input: {search:{}}

};this._createWidgets();this._routeTraffic();

},_routeTraffic: function () {

this._on(this.element, { autocompletesuccess: this._showOptionList });this._on(this._widgets.search, { inputkeydown: _.debounce(this._updateDataloaderSearchParam, 100) });this._on(this._widgets.results, { optionlistselected: this._updateInput });

},_updateDataloaderSearchParam: function (e, search) {

var deferred = this._widgets.loader .dataloader(‘updateParams’, ‘search’, search) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},_showOptionList: function () {

this._widgets.results.optionlist(‘show);this._trigger(‘showresults’);

},_updateInput: function (e, value) {

this._widgets.search.input(‘setValue’, value);this._trigger(‘change’, value);

},setData: function (data) {

var deferred = this._widgets.loader .dataloader(‘setData’, data) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},setValue: function (value) {

this._updateInput(null, value);}

});

$(function () {$(‘abc-autocomplete’).autocomplete();

});

Page 12: Getting the Most Out of jQuery Widgets

$.widget(‘abc.autocomplete’, {_create: function () {

this._widgets = {dataloader: {loader:{}},optionlist: {ddl:{}},input: {search:{}}

};this._createWidgets();this._routeTraffic();

},_routeTraffic: function () {

this._on(this.element, { autocompletesuccess: this._showOptionList });this._on(this._widgets.search, { inputkeydown: _.debounce(this._updateDataloaderSearchParam, 100) });this._on(this._widgets.results, { optionlistselected: this._updateInput });

},_updateDataloaderSearchParam: function (e, search) {

var deferred = this._widgets.loader .dataloader(‘updateParams’, ‘search’, search) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},_showOptionList: function () {

this._widgets.results.optionlist(‘show);this._trigger(‘showresults’);

},_updateInput: function (e, value) {

this._widgets.search.input(‘setValue’, value);this._trigger(‘change’, value);

},setData: function (data) {

var deferred = this._widgets.loader .dataloader(‘setData’, data) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},setValue: function (value) {

this._updateInput(null, value);}

});

$(function () {$(‘abc-autocomplete’).autocomplete();

});

Page 13: Getting the Most Out of jQuery Widgets

$.widget(‘abc.autocomplete’, {_create: function () {

this._widgets = {dataloader: {loader:{}},optionlist: {ddl:{}},input: {search:{}}

};this._createWidgets();this._routeTraffic();

},_routeTraffic: function () {

this._on(this.element, { autocompletesuccess: this._showOptionList });this._on(this._widgets.search, { inputkeydown: _.debounce(this._updateDataloaderSearchParam, 100) });this._on(this._widgets.results, { optionlistselected: this._updateInput });

},_updateDataloaderSearchParam: function (e, search) {

var deferred = this._widgets.loader .dataloader(‘updateParams’, ‘search’, search) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},_showOptionList: function () {

this._widgets.results.optionlist(‘show);this._trigger(‘showresults’);

},_updateInput: function (e, value) {

this._widgets.search.input(‘setValue’, value);this._trigger(‘change’, value);

},setData: function (data) {

var deferred = this._widgets.loader .dataloader(‘setData’, data) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},setValue: function (value) {

this._updateInput(null, value);}

});

$(function () {$(‘abc-autocomplete’).autocomplete();

});

Page 14: Getting the Most Out of jQuery Widgets

$.widget(‘abc.autocomplete’, {_create: function () {

this._widgets = {dataloader: {loader:{}},optionlist: {ddl:{}},input: {search:{}}

};this._createWidgets();this._routeTraffic();

},_routeTraffic: function () {

this._widgets.loader.on(‘dataloadersuccess’, this._updateDropdownlist);this._widgets.ddl.on(‘dropdownlistselected’, this._updateInput);this._widgets.search.on(‘inputkeydown’, _.debounce(this._updateDataloaderSearchParam, 300));

},_updateDataloaderSearchParam: function (e, search) {

var deferred = this._widgets.loader .dataloader(‘updateParams’, ‘search’, search) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},_showOptionList: function () {

this._widgets.results.optionlist(‘show);this._trigger(‘showresults’);

},_updateInput: function (e, value) {

this._widgets.search.input(‘setValue’, value);this._trigger(‘change’, value);

},setData: function (data) {

var deferred = this._widgets.loader .dataloader(‘setData’, data) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},setValue: function (value) {

this._updateInput(null, value);}

});

$(function () {$(‘abc-autocomplete’).autocomplete();

});

Page 15: Getting the Most Out of jQuery Widgets

$.widget(‘abc.autocomplete’, {_create: function () {

this._widgets = {dataloader: {loader:{}},optionlist: {ddl:{}},input: {search:{}}

};this._createWidgets();this._routeTraffic();

},_routeTraffic: function () {

this._widgets.loader.on(‘dataloadersuccess’, this._updateDropdownlist);this._widgets.ddl.on(‘dropdownlistselected’, this._updateInput);this._widgets.search.on(‘inputkeydown’, _.debounce(this._updateDataloaderSearchParam, 300));

},_updateDataloaderSearchParam: function (e, search) {

var deferred = this._widgets.loader .dataloader(‘updateParams’, ‘search’, search) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},_showOptionList: function () {

this._widgets.results.optionlist(‘show);this._trigger(‘showresults’);

},_updateInput: function (e, value) {

this._widgets.search.input(‘setValue’, value);this._trigger(‘change’, value);

},setData: function (data) {

var deferred = this._widgets.loader .dataloader(‘setData’, data) .dataloader(‘fetch’);

this._trigger(‘fetch’, deferred);},setValue: function (value) {

this._updateInput(null, value);}

});

$(function () {$(‘abc-autocomplete’).autocomplete();

});

Page 16: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

BAD IDEAAHEAD

Page 17: Getting the Most Out of jQuery Widgets

Decorate ALL thefunction

s!

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 18: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

MODIFY THE FACTORY FUNCTION IF YOU NEED TO

Decorate ALL thefunction

s!

Page 19: Getting the Most Out of jQuery Widgets

var widgetFactory = $.widget;

$.widget = function (name, base, prototype) {var targetPrototype = prototype || base;

$.each(targetPrototype, function (key, callback) {if (typeof callback === ‘function’) {

targetPrototype[key] = function () {if (someConditionPasses) {fireSomeFunction();}

var result = callback.apply(this, arguments);

if (someOtherConditionPasses) {fireSomeOtherFunction();}

return result;};

}});return widgetFactory.apply(this, arguments);

};

// The widget factory function itself has some function members itself,// like $.widget.bridge and $.widget.extend. Don’t forget to copy those// items over from the original factory to our new implementation!$.each(widgetFactory, function (key, value) {

$.widget[key] = value;});

Page 20: Getting the Most Out of jQuery Widgets

var widgetFactory = $.widget;

$.widget = function (name, base, prototype) {var targetPrototype = prototype || base;

$.each(targetPrototype, function (key, callback) {if (typeof callback === ‘function’) {

targetPrototype[key] = function () {if (someConditionPasses) {fireSomeFunction();}

var result = callback.apply(this, arguments);

if (someOtherConditionPasses) {fireSomeOtherFunction();}

return result;};

}});return widgetFactory.apply(this, arguments);

};

// The widget factory function itself has some function members itself,// like $.widget.bridge and $.widget.extend. Don’t forget to copy those// items over from the original factory to our new implementation!$.each(widgetFactory, function (key, value) {

$.widget[key] = value;});

Page 21: Getting the Most Out of jQuery Widgets

var widgetFactory = $.widget;

$.widget = function (name, base, prototype) {var targetPrototype = prototype || base;

$.each(targetPrototype, function (key, callback) {if (typeof callback === ‘function’) {

targetPrototype[key] = function () {if (someConditionPasses) {fireSomeFunction();}

var result = callback.apply(this, arguments);

if (someOtherConditionPasses) {fireSomeOtherFunction();}

return result;};

}});return widgetFactory.apply(this, arguments);

};

// The widget factory function itself has some function members itself,// like $.widget.bridge and $.widget.extend. Don’t forget to copy those// items over from the original factory to our new implementation!$.each(widgetFactory, function (key, value) {

$.widget[key] = value;});

Page 22: Getting the Most Out of jQuery Widgets

var widgetFactory = $.widget;

$.widget = function (name, base, prototype) {var targetPrototype = prototype || base;

$.each(targetPrototype, function (key, callback) {if (typeof callback === ‘function’) {

targetPrototype[key] = function () {if (someConditionPasses) {fireSomeFunction();}

var result = callback.apply(this, arguments);

if (someOtherConditionPasses) {fireSomeOtherFunction();}

return result;};

}});return widgetFactory.apply(this, arguments);

};

// The widget factory function itself has some function members itself,// like $.widget.bridge and $.widget.extend. Don’t forget to copy those// items over from the original factory to our new implementation!$.each(widgetFactory, function (key, value) {

$.widget[key] = value;});

Page 23: Getting the Most Out of jQuery Widgets

var widgetFactory = $.widget;

$.widget = function (name, base, prototype) {var targetPrototype = prototype || base;

$.each(targetPrototype, function (key, callback) {if (typeof callback === ‘function’) {

targetPrototype[key] = function () {if (someConditionPasses) {fireSomeFunction();}

var result = callback.apply(this, arguments);

if (someOtherConditionPasses) {fireSomeOtherFunction();}

return result;};

}});return widgetFactory.apply(this, arguments);

};

// The widget factory function itself has some function members itself,// like $.widget.bridge and $.widget.extend. Don’t forget to copy those// items over from the original factory to our new implementation!$.each(widgetFactory, function (key, value) {

$.widget[key] = value;});

Page 24: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

ALWAYS TRY TO USE PUBLIC API FOR FORWARD COMPATIBILITY

Decorate ALL thefunction

s!

Page 25: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

WHO CARES ABOUT INTERNAL IMPLEMENTATIONS?

Feel free to

mix it up.

Page 26: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

OVERRIDE FUNCTIONALITY IN ONE OF TWO WAYS:

Feel free to

mix it up.

$.widget Factory

Widget Options

• Overrides prototype, affects all instances

• Maintains pointer to overridden function via _super and _superApply

• Overrides instance-level functionality only

• Provides easy access to consumers to override functionality

Page 27: Getting the Most Out of jQuery Widgets

$.widget(‘abc.dataloader’, {options: {url: null,success: function (results) {this.element.html(JSON.stringify(results));},// etc},fetch: function () {this.element.addClass(‘loading’);return this._load().done($.proxy(function (results) {this.options.success.call(this, results);}, this).always($.proxy(function () {this.element.removeClass(‘loading’);}, this));},_load: function () {return $.ajax(this.options);}

});$.widget(‘abc.dataloader’, abc.dataloader, {

_load: function () {var deferred = $.Deferred();this.element.data(‘backboneCollection’).fetch({reset: true,success: function (collection) {deferred.resolve(collection.toJSON());},error: function (collection, response) {deferred.reject(response);}});return deferred.promise();}

});

var myTemplate = Handlebars.compile($(‘#myTemplate’).html());$(‘#myDiv’).dataloader({

success: function (results) {this.element.html(myTemplate(results));}

});

Page 28: Getting the Most Out of jQuery Widgets

$.widget(‘abc.dataloader’, {options: {url: null,success: function (results) {this.element.html(JSON.stringify(results));},// etc},fetch: function () {this.element.addClass(‘loading’);return this._load().done($.proxy(function (results) {this.options.success.call(this, results);}, this).always($.proxy(function () {this.element.removeClass(‘loading’);}, this));},_load: function () {return $.ajax(this.options);}

});$.widget(‘abc.dataloader’, abc.dataloader, {

_load: function () {var deferred = $.Deferred();this.element.data(‘backboneCollection’).fetch({reset: true,success: function (collection) {deferred.resolve(collection.toJSON());},error: function (collection, response) {deferred.reject(response);}});return deferred.promise();}

});

var myTemplate = Handlebars.compile($(‘#myTemplate’).html());$(‘#myDiv’).dataloader({

success: function (results) {this.element.html(myTemplate(results));}

});

Page 29: Getting the Most Out of jQuery Widgets

$.widget(‘abc.dataloader’, {options: {

url: null,success: function (results) {this.element.html(JSON.stringify(results));},// etc

},fetch: function () {

this.element.addClass(‘loading’);return this._load().done($.proxy(function (results) {this.options.success.call(this, results);}, this).always($.proxy(function () {this.element.removeClass(‘loading’);}, this));

},_load: function () {

return $.ajax(this.options);}

});$.widget(‘abc.dataloader’, abc.dataloader, {

_load: function () {var deferred = $.Deferred();this.element.data(‘backboneCollection’).fetch({reset: true,success: function (collection) {deferred.resolve(collection.toJSON());},error: function (collection, response) {deferred.reject(response);}});return deferred.promise();

}});

var myTemplate = Handlebars.compile($(‘#myTemplate’).html());$(‘#myDiv’).dataloader({

success: function (results) {this.element.html(myTemplate(results));

}});

Page 30: Getting the Most Out of jQuery Widgets

$.widget(‘abc.dataloader’, {options: {url: null,success: function (results) {this.element.html(JSON.stringify(results));},// etc},fetch: function () {this.element.addClass(‘loading’);return this._load().done($.proxy(function (results) {this.options.success.call(this, results);}, this).always($.proxy(function () {this.element.removeClass(‘loading’);}, this));},_load: function () {return $.ajax(this.options);}

});$.widget(‘abc.dataloader’, abc.dataloader, {

_load: function () {var deferred = $.Deferred();this.element.data(‘backboneCollection’).fetch({reset: true,success: function (collection) {deferred.resolve(collection.toJSON());},error: function (collection, response) {deferred.reject(response);}});return deferred.promise();}

});

var myTemplate = Handlebars.compile($(‘#myTemplate’).html());$(‘#myDiv’).dataloader({

success: function (results) {this.element.html(myTemplate(results));}

});

Page 31: Getting the Most Out of jQuery Widgets

Make it

testable!

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 32: Getting the Most Out of jQuery Widgets

Make it

testable!DOES IT PERFORM A LOGICAL OPERATION OR CALCULATION? / IS IT PART OF THE WIDGET’S PUBLIC-FACING API?

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 33: Getting the Most Out of jQuery Widgets

Make it

testable!DOES IT PERFORM A LOGICAL OPERATION OR CALCULATION? / IS IT PART OF THE WIDGET’S PUBLIC-FACING API?

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 34: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

PUBLIC FUNCTIONS SHOULD HAVE UNIT TESTS / STORE PROTOTYPES IN OBJECT NAMESPACES / TEST LOGICAL FUNCTIONS SEPARATELY

expose it!

Page 35: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

PUBLIC FUNCTIONS SHOULD HAVE UNIT TESTS / STORE PROTOTYPES IN OBJECT NAMESPACES / TEST LOGICAL FUNCTIONS SEPARATELY

expose it!

Page 36: Getting the Most Out of jQuery Widgets

Richard Lindsey @Velveeta http://conqueringtheclient.com/

PUBLIC FUNCTIONS SHOULD HAVE UNIT TESTS / STORE PROTOTYPES IN OBJECT NAMESPACES / TEST LOGICAL FUNCTIONS SEPARATELY

expose it!

Page 37: Getting the Most Out of jQuery Widgets

ABC = {};(function ($) {

ABC.Prototypes = ABC.Prototypes || {};ABC.Prototypes.demo = {

_create: function () {if (this. _getInstanceCount() === 1) {this._attachListeners();}},_getInstanceCount: function () {return $(‘:abc-demo’).length;},_attachListeners: function () {$(‘body’).on(‘click.demo’, ‘:abc-demo’, $.proxy(this._clickHandler, this));},_clickHandler: function () {console.log(this._getInstanceCount() + ‘ demo widgets instantiated!’);},destroy: function () {if (this._getInstanceCount() === 1) {$(‘body’).off(‘.demo’);this._super();}}

};$(function () {

$(‘.demo’).demo();});

}(jQuery));(function ($) {

$.each(ABC.Prototypes, function (widgetName, widgetPrototype) {$.widget(‘abc.’ + widgetName, widgetPrototype);

});}(jQuery));

Page 38: Getting the Most Out of jQuery Widgets

ABC = {};(function ($) {

ABC.Prototypes = ABC.Prototypes || {};ABC.Prototypes.demo = {

_create: function () {if (this. _getInstanceCount() === 1) {this._attachListeners();}},_getInstanceCount: function () {return $(‘:abc-demo’).length;},_attachListeners: function () {$(‘body’).on(‘click.demo’, ‘:abc-demo’, $.proxy(this._clickHandler, this));},_clickHandler: function () {console.log(this._getInstanceCount() + ‘ demo widgets instantiated!’);},destroy: function () {if (this._getInstanceCount() === 1) {$(‘body’).off(‘.demo’);this._super();}}

};$(function () {

$(‘.demo’).demo();});

}(jQuery));(function ($) {

$.each(ABC.Prototypes, function (widgetName, widgetPrototype) {$.widget(‘abc.’ + widgetName, widgetPrototype);

});}(jQuery));

Page 39: Getting the Most Out of jQuery Widgets

ABC = {};(function ($) {

ABC.Prototypes = ABC.Prototypes || {};ABC.Prototypes.demo = {

_create: function () {if (this. _getInstanceCount() === 1) {this._attachListeners();}},_getInstanceCount: function () {return $(‘:abc-demo’).length;},_attachListeners: function () {$(‘body’).on(‘click.demo’, ‘:abc-demo’, $.proxy(this._clickHandler, this));},_clickHandler: function () {console.log(this._getInstanceCount() + ‘ demo widgets instantiated!’);},destroy: function () {if (this._getInstanceCount() === 1) {$(‘body’).off(‘.demo’);this._super();}}

};$(function () {

$(‘.demo’).demo();});

}(jQuery));(function ($) {

$.each(ABC.Prototypes, function (widgetName, widgetPrototype) {$.widget(‘abc.’ + widgetName, widgetPrototype);

});}(jQuery));

Page 40: Getting the Most Out of jQuery Widgets

module(‘demo core’);

test(‘_getInstanceCount’, 2, function () {var container = $(‘<div></div>’).appendTo(‘body’),

deferred = $.Deferred(),myDemo;

stop();

deferred.done(function (instanceCount) {equal(instanceCount, 1, ‘Returns proper value with 1 instance’);}).fail(function () {ok(false, ‘Returns proper value with 1 instance’);}).always(function () {container.remove();start();});

equal(ABC.Prototypes.demo._getInstanceCount(), 0, ‘Returns proper value with no instances’);

myDemo = $(‘<div></div>’).appendTo(container).on(‘democreate’, function () {deferred.resolve(ABC.Prototypes.demo._getInstanceCount());}).demo();

setTimeout(function () {deferred.reject();

}, 250);});

Page 41: Getting the Most Out of jQuery Widgets

module(‘demo core’);

test(‘_getInstanceCount’, 2, function () {var container = $(‘<div></div>’).appendTo(‘body’),

deferred = $.Deferred(),myDemo;

stop();

deferred.done(function (instanceCount) {equal(instanceCount, 1, ‘Returns proper value with 1 instance’);}).fail(function () {ok(false, ‘Returns proper value with 1 instance’);}).always(function () {container.remove();start();});

equal(ABC.Prototypes.demo._getInstanceCount(), 0, ‘Returns proper value with no instances’);

myDemo = $(‘<div></div>’).appendTo(container).on(‘democreate’, function () {deferred.resolve(ABC.Prototypes.demo._getInstanceCount());}).demo();

setTimeout(function () {deferred.reject();

}, 250);});

Page 42: Getting the Most Out of jQuery Widgets

module(‘demo core’);

test(‘_getInstanceCount’, 2, function () {var container = $(‘<div></div>’).appendTo(‘body’),

deferred = $.Deferred(),myDemo;

stop();

deferred.done(function (instanceCount) {equal(instanceCount, 1, ‘Returns proper value with 1 instance’);}).fail(function () {ok(false, ‘Returns proper value with 1 instance’);}).always(function () {container.remove();start();});

equal(ABC.Prototypes.demo._getInstanceCount(), 0, ‘Returns proper value with no instances’);

myDemo = $(‘<div></div>’).appendTo(container).on(‘democreate’, function () {deferred.resolve(ABC.Prototypes.demo._getInstanceCount());}).demo();

setTimeout(function () {deferred.reject();

}, 250);});

Page 43: Getting the Most Out of jQuery Widgets

module(‘demo core’);

test(‘_getInstanceCount’, 2, function () {var container = $(‘<div></div>’).appendTo(‘body’),

deferred = $.Deferred(),myDemo;

stop();

deferred.done(function (instanceCount) {equal(instanceCount, 1, ‘Returns proper value with 1 instance’);}).fail(function () {ok(false, ‘Returns proper value with 1 instance’);}).always(function () {container.remove();start();});

equal(ABC.Prototypes.demo._getInstanceCount(), 0, ‘Returns proper value with no instances’);

myDemo = $(‘<div></div>’).appendTo(container).on(‘democreate’, function () {deferred.resolve(ABC.Prototypes.demo._getInstanceCount());}).demo();

setTimeout(function () {deferred.reject();

}, 250);});

Page 44: Getting the Most Out of jQuery Widgets

Wrap it up already, will ya?

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Page 45: Getting the Most Out of jQuery Widgets

ONLY MAKE COMPONENTS AS LARGE AS THEY NEED TO BE / KEEP THEM AS DECOUPLED AS POSSIBLE / CONSUME DOWNWARDS, COMMUNICATE UPWARDS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Wrap it up

already…

Page 46: Getting the Most Out of jQuery Widgets

ONLY MAKE COMPONENTS AS LARGE AS THEY NEED TO BE / KEEP THEM AS DECOUPLED AS POSSIBLE / CONSUME DOWNWARDS, COMMUNICATE UPWARDS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Wrap it up

already…

Page 47: Getting the Most Out of jQuery Widgets

ONLY MAKE COMPONENTS AS LARGE AS THEY NEED TO BE / KEEP THEM AS DECOUPLED AS POSSIBLE / CONSUME DOWNWARDS, COMMUNICATE UPWARDS

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Wrap it up

already…

Page 48: Getting the Most Out of jQuery Widgets

DECORATE THE FACTORY, BUT BE CAREFUL ABOUT TYING TO IMPLEMENTATIONS.

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Wrap it up

already…

Page 49: Getting the Most Out of jQuery Widgets

MAKE FUNCTIONS & OPTIONS GRANULAR AND ROBUST FOR POTENTIAL OVERRIDES.

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Wrap it up

already…

Page 50: Getting the Most Out of jQuery Widgets

TEST, TEST, AND TEST! MAKE EVERY ATTEMPT TO ENSURE BACKWARD COMPATIBILITY FOR CONSUMERS.

Richard Lindsey @Velveeta http://conqueringtheclient.com/

Wrap it up

already…

Page 51: Getting the Most Out of jQuery Widgets

thanks!Presentation available online: http://bit.ly/jqwidgets

Richard Lindsey @velveeta http://conqueringtheclient.com/PLATFORM FEE ARCHITECT | THE ADVISORY BOARD COMPANY