seanmonstar

Mar 9 2011

Dec 8 2010

Nov 12 2010

Why Object.prototype is not the place for Hash methods

You might have heard that extending the Object.protoype is verboten. But you might realize that one of the most famous issues with extending Object can be solved with the handy obj.hasOwnProperty(key) line. Besides, developers who like picking MooTools likely do so (at least partly) because it’s much more elegant to call methods on objects than pass objects into functions. I’ve been asked why MooTools choses to keep a separate Hash class instead of putting those methods directly on the Object prototype. Here’s my reasoning for why.

Object is an Easy Hash

In JavaScript, the object literal notation makes it very easy to create hash-like structures. However, it does not provide that hash with methods to access keys, or do a forEach loop over, do a map function, and the like. Some developers might reach for the Object’s prototype to add in these useful methods. However, you might remember that all objects inherit from Object. Which essentially means everything (Array, Function, user-defined class).

Dog isn’t a Hash

Now imagine, defining a class like Dog. Or how about User, since that’s much more real-world. Does it make sense for User to inherit Hash methods? Would you in any other language write class User extends Hash? Can you imagine being able to call someUser.map(fn) or someUser.keys()? It’s silly!

You might be thinking that it doesn’t matter, you would never need to make those method calls, so they’re practically invisible. But they’re not. The point of decent code is modularize information and logic, not mix in pieces we never need.

It’s especially easy to just include the Hash class from MooTools More if you need hash capabilities. It’s 6 extra characters around your object literal, and then you don’t pollute any other class. Hurray!


Oct 26 2010

Oct 18 2010

MVC in MooTools: Controllers

This is another installment about how to use my MooTools MVC framework. If you’re interested in the previous parts, check out my write-up on using Models and Views.

It’s often explained that Controllers are how you tie Models and Views together. I don’t want to get into that argument. For me and my framework, Controllers handle DOM events. They listen to the views for events, update models, and then render new views. Let’s take a look.

Event.Delegation

Event delegation is used throughout the controllers, since it greatly eases the pain of changing the DOM around all the time. When you name a controller, the first part of the name will be used to find or create an element to look over, unless another element is specified. Example time:

var RecipesController = new Class({
    Extends: Controller
});

//attaches itself to <div id="Recipes"></div>
//if that doesn't exist, it gets created

The rest of the controller contains event handlers for everything that goes on inside that element, and methods that various event handlers can call.

var RecipesController = new Class({
    Extends: Controller,
    events: {
        load: function() {
            Recipe.findAll(this.list.bind(this));
        },
        'click:relay(.recipe .delete)': function(e) {
            e.preventDefault();
            var recipeView = $(e.target).getParent('.recipe');
            recipeView.get('model').destroy(function() {
                recipeView.destroy();
            });
        }
    }
});

The load event will fire on domready, and eveything else is in the MooTools event delegation way. Currently, all event handlers are bound to the controller instead of the element, since it’s easy enough to get the element from the event object, and you’re most likely going to want to call a method of the controller. For instance, you might notice that the load event has a call to list on the controller.

this.view

First-class methods on a controller get access to the view method. This is a convenience method that will instantiate views for you, with a little auto-magic thrown in.

var RecipesController = new Class({
    Extends: Controller,
    // events: {},
    list: function(recipes) {
        $(this).set('html', this.view({ recipes: recipes }));
    }
});

view can accept 2 parameters: the view name, and a data object. However, you can exclude the view name, and it will try to use the controller and method names. In the list method above, since no view name is passed to view, it will try to load recipes/list.

That’s About It

That’s all there is to Controllers, actually. They just handled events fired by Views, let appropriate Models know, and change the View if necessary. Although, while the role seems small, it tends to contain the majority of logic anyways. Models just hold on to their data, and Views just show it, but Controllers are how you manipulate it.


Oct 11 2010