the open web, web development, and more

Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Monday, July 20, 2009

Methods Plugin 0.1

Since my introductory post on the Methods Plugin, I've improved the API and published it to jQuery's Plugin Directory.

The latest 0.1 version now adds 2 methods to jQuery: addMethod for adding methods and methods for retrieving the methods you added. Let's jump into some example code to explain it better. In this example I'll create a function that returns a jQuery object which has custom methods added to it. These methods can only be called by first calling the methods method on the jQuery object.

/**
* A generic productListing widget that can be displayed in a list...
*
* @param {String} tag - A string of HTML that will be converted to a jQuery Object
* @param {Object} data - A JSON Object of all the data, e.g.: {name: string, quantity: integer, price: float, description: string, freeShipping: boolean}
*/
function productListing(tag, data) {
var $el = $(tag);

$el
.addClass("product-listing")
.append("<h3>" + data.name + "</h3>")
.append("<p>" + data.description + "</p>")
.append('<span class="product-listing-quantity" >' + data.quantity + '</span>')
.append('<span class="product-listing-price" >' + data.price + '</span>')
// And on and on doing DOM stuff.
// Now here is the interesting part!
.addMethod("getQuantity", function () {
return data.quantity;
})
.addMethod("setQuantity", function (quantity) {
data.quantity = quantity;
$el.find(".product-listing-quantity").html(quantity);
})
.addMethod("freeShipping", function () {
return data.freeShipping;
});
}
So now we have a productListing function that returns a jQuery object. This object can be inserted into the DOM, hidden, animated, or anything else you like to do with jQuery. However, you can also call the methods method, which returns a simple object of your functions, and start calling any of the methods you added previously: getQuantity, setQuantity, and freeShipping. If any of these methods does not return a value, the methods object is returned so you can continue to call methods in a chain. You can also call the method end to get back to the jQuery objects scope (included for chainability).

To explain this better, let's get back to code:
// Some data or our productListing
var data = {
name: "Internet Celebrities Spoon Set",
description: "Now get the Star Wars Kid on a coffee spoon! These commemorative spoons...",
quantity: 10,
price: 19.99,
freeShipping: false
};

// Create a new productListing object
var commemorativeSpoonSet = productListing("<li/>", data);

// Append it to a DOM node, manipulate it, and call custom methods
$("#products-list")
.append(commemorativeSpoonSet)
.hide()
.methods()
.setQuantity(20)
.end()
.show();

// Look it up in the DOM too
var products = $(".product-listing");

products.each(function (i) {
if (!$(this).methods().freeShipping())
{
$(this).hide();
}
});
And there you have it. Methods is a small plugin, but hopefully it makes your life as a JavaScript developer a little easier.

Wednesday, July 8, 2009

Better Objects Using jQuery

I've been working almost exclusively with JavaScript building single page applications recently. jQuery has made developing the apps enjoyable but one area I find a bit awkward is creating complex objects.

Classes work well but can be cumbersome when creating a user interface since the object reference returned from the constructor is not a DOM node but a JavaScript object. Therefore, in order to insert the user interface component into the page, a special method like getHTML must be available for that object. This isn't terrible but creates more complicated code. Here's an example user interface widget - a simple box displaying a name:

function Widget(name) {
var container = $("<div/>");

function setName(newName) {
name = newName;
container.html(name);
}

this.getHTML = function () {
setName(name);
return container;
};

this.getName = function () {
return name;
};

this.setName = function (newName) {
setName(name);
};
}

And the code that uses it:

var nameWidget = new Widget("Dave");
var widgetNode = nameWidget.getHTML();
$(document.body).append(widgetNode);

This isn't too bad but gets a bit ugly when you need the ability to hide, show, animate, or do anything interesting. The choice is either to attach more methods to the Widget class or work with the jQuery object returned by getHTML. Pattern-wise, adding more methods to Widget is the cleanest but could require creating a lot of methods. Working with the jQuery object returned by getHTML is easy but leads to managing two objects in your code: nameWidget and widgetNode

What would be really nice is the ability to call any jQuery method on the nameWidget object PLUS all the methods specific to the Widget class. It occurred to me that this could be possible with the addition of a small jQuery Plugin I've dubbed Methods.

(function ($) {

$.fn.methods = function (arg0) {
if (arg0)
{
return this.each(function (i) {
$(this).data("methods", arg0);
});
}
else
{
return $(this).data("methods");
}
};

})(jQuery);

This plugin adds the methods method to jQuery. A collection of functions can be passed to methods to attach it to a specific jQuery object. Calling methods then returns the collection of functions. Here's a rewrite of the Widget class to explain this better:

function widget(tag, name) {
var container;

container = $(tag)
.html(name)
.methods({
getName: function () {
return name;
},

// Return "this" for chainability
setName: function (newName) {
name = newName;
container.html(name);
return this;
},

// Add the end method for chainability
end: function () {
return container;
}
});
return container;
}

And the code that uses this:

var nameWigdet = widget("<div/>", "Dave");
$(document.body).append(nameWidget);

Now we have a nameWidget that directly corresponds to the user interface component and, using jQuery, can be inserted anywhere in the page. To access the object's specific methods, you simply call methods. Here's an example that shows the combination of the methods and chaining:

nameWidget
.hide()
.methods()
.setName("Bob")
.end()
.show()
.addClass("widget");

Since the Methods plugin uses jQuery's data method behind the scenes, the nameWidget can actually be retrieved through a DOM lookup.

var nameWidget = $(".widget")
.hide()
.methods()
.setName("Sally")
.end()
.show();

And that's it. In the end, Methods is a simple jQuery plugin that lets you create complex objects with custom methods and closures and can still be used like a regular jQuery object.

Friday, November 7, 2008

Enumerating Objects in JavaScript

Enumerating through objects is something I only occassionally do in JavaScript, but when it does come up, I always find it a bit unintuitive. Here's some sample code for the future me that is faced with this task again:

var dataObject = {
foo: "bar",
one: "fish",
hello: "world"
};

var key;
for (key in dataObject)
{
document.write("Key: " + key ", Value: " + dataObject[key]);
document.write("\n");
}

This code will produce the following lines:
Key: foo, Value: bar
Key: one, Value: fish
Key: hello, Value: world