Tuesday, February 9, 2010

I was playing around with creating Jquery plugins a while ago.  I wanted to comment on this for others.  It turns out making plugins is rather easy.  Jquery is just the coolest javascript library I have ever seen.  There are a number of good templates online and grabbing a simple template and extending it is the easiest thing to do.  I found this one by Dave Smith and you should also refer to the Official Docs.  The reason I like a simple template to start with like the one Dave has is sometimes the official docs since they have to cover every options do not explain how to get started very well.

One comment on Daves template is that one change should be to make the functions that all functions that are not "get" functions should return a reference to "this" so you can chain function (see example).

So basically here is my (slightly modified template):


(function($)
{
$.fn.plugin = function(options) {
  // Set the options.
  options = $.extend({}, $.fn.plugin.defaults, options);
  // Go through the matched elements and return the jQuery object.
  return this.each(function()
  {
  });
};
// Public defaults.
$.fn.plugin.defaults = {
property: 'value'
};
// Private functions.
function func()
{
return;
};
// Public functions.
$.fn.plugin.func = function()
{
return this;
};
})(jQuery);


Now for an example.  Say we wanted to make a trivial template that sets all elements using this plugins color to red and enables you to change the font color and the background color.  You can take the template above and change it like this:



(function($) {  
$.fn.mycolor = function(options)  {   
// Set the options.   
options = $.extend({}, $.fn.plugin.defaults, options);    

// Go through the matched elements and return the jQuery object.   
return this.each(function()   {
// do something with this. Say we wanted to make it red
this.css("color", options.color);
});  
};
// Public defaults.  
$.fn.mycolor.defaults = { 
color: 'red' 
};
// Public functions.  
$.fn.mycolor.setColor = function(color)  {
this.css("color", color);   
return this;  
}; 

$.fn.mycolor.setBackgroundColor = function()  {
this.css("background-color", color);   
return this;  
};
})(jQuery);



Now you can do this:


$(".color_example").mycolor();


This will make all html elements with the class .color_example change colors to red, then to change their colors later in some other context you can do this:


$(".color_example").setColor("blue").setBackgroundColor("black");

Note that unless setColor returned "this" you would have to do this in two function calls.  This is so stupidly trivial it is not useful but hopefully this helps get the idea of how easy it is to make a plugin and do cool things with it.

Tuesday, February 2, 2010

Over the couple years I have been using Zend Framework to an increasing degree in projects I have been working on.  At first I just used a small set of Zend Frameworks features.  For example some other developers and I liked the way the form validation was set up so we started using Zend validators in our forms, we used Zend_Db_Table, Zend_Acl,  and other various components.  Over time we started using the full fledged Zend Framework MVC system enabling us to slowly weed out various proprietary libraries we had developed over time.

For the most part my experience with Zend Framework has been positive.  The framework is well documented relatively easy to use, is well tested and designed but there is one aspect of Zend Framework which seems very non-intuitive and confusing to work with, namely Form Decorators.

Form Decorators seem like a very cool idea in theory.  The idea of defining a form as a sort of specification that can be rendered dynamically in such a way that the view of the form is abstracted from the specification opens some cool doors.  For example we can start to think of every form element now as a basic Object which can be reused in many forms on many pages within a given site and even reused across multiple sites.  The validation for the element is tied to the element and is therefore inherited in each form it exists within.

Consider some common form elements like address, postal code, email address, country, state, etc.  All such elements with some minor variation are basically the same in most forms you would put them in.  Country and States usually need to be populated from a database so the element can also contain the logic for fetching the list from the database and populating it's internal array of options.  States are usually contextual to the country so you need to add a trigger to the the country element to update the state list when a country is selected.

I have been fairly successful tying this together with JavaScript and placing all of the logic and configurations for all of these kinds of things with my element classes.  My favourite library for client side JavaScript is Jquery which in my opinion is one of the coolest things to happen to JavaScript ever.  When I think back to the browser war where Microsoft was trying to snuff out Netscape.  We used to have to create all these libraries to abstract basic functions so they would work in entirely different ways depending on the platform, then a new version would come out and break it.

The W3C standards that are supported by all modern browsers has helped make it possible to write portable JavaScript code without the need for such libraries but Jquery is not so much a wrapper library to translate code to various platforms like we did in the old days it is a new way of thinking about JavaScript, it is a way of doing a lot with a few lines of code.  The difference between writing code in plain JavaScript and with Jquery is sort of like the difference between writing procedural code to look for patterns in textual strings vs. using regular expressions.  It is just far cooler and more efficient.

Anyway enough on that subject back to Zend Forms and decorators.  So hopefully the idea is clear.  Form elements can contain a common and reusable payload which defines attributes of such elements and forms are like a bundle of such elements.  Zend_Form contains various options for creating groups of elements wrapped in a fieldset and such forth but like I said the one thing that seems a bit counter intuitive is how decorators work.

At first I would google for "Zend_Form decorators table" for instance and find examples on how to render a form in a table or whatever but the problem is that while many examples exist it is always a pain to find one that actually does exactly what you want.  Furthermore without some understanding of fundamentally how they work it is difficult to modify them to do what you want.  Even if you find an example that is close to what you want it is never exactly the same so some modification is almost always required.

Some developers recommend just bypassing the form rendering methods built in to Zend_Form and just building custom views for each form and just dynamically rendering the form elements and labels manually in the template because it is so weirdly complicated and annoying to deal with.  The problem with this is you have now thrown away all of the coolness and abstraction that has been carefully set up in Zend_Form.  Now a form is tied to a view and you cannot change one without also changing the other.

It seems to me if you have to manage the code in more then one place you might as well not use Zend Form.  Just do it the old fashioned way and make a page with hard-coded HTML form elements and write code to handle the post variables that come in.  I guess I can think of some exceptions where Zend_Form coupled with customized views makes sense but for the most part it seems like one ought to just be able to work with decorators and make the form render the way you want.

In any case this post is mostly meant to phrase the problem and put it out there.  Over the next week I plan on looking over the Zend Framework source code and see if I can make some sense of how to use decorators.  The various how to's and forum entries on the web answer only part of the question of how to use decorators and only usually within some context which does not give you a complete picture.  I will try to follow up when I have time hopefully with a clear answer of how the heck they are supposed to work.