views:

52

answers:

2

I have developed a little javscript widget to turn some nested <ul> blocks into a windows explorer style browser. I have recently learnt about the object literal pattern and decided to give it a go, so the organisation of my code is something like this:

var myExplorer = {
    init : function(settings) {
        myExplorer.config = {
            $wrapper : $('#explorerCategories'),
            $contentHolder : $j('#categoryContent'),
            loadingImg : '<img src="../images/standard/misc/ajax_loader.gif" alt="loading" class="loading" />'
        }
        // provide for custom configuration via init()
        if (settings && typeof(settings) == 'object') {
            $.extend(myExplorer.config, settings);
        }

        // some more code...
    },

    createExpanderLink : function() {
        // more code
    },

    anotherMethod : function() {
        // etc
    }
}

Then in my page I set up my explorer with:

$j(function () {
    myExplorer.init();
}

This all works fine by the way. The problem is when I want to have more then one of these explorer style widgets on the same page. I tried passing in the different settings:

$j(function () {
    // first instance
    myExplorer.init();

    //second instance
    var settings = {
        $wrapper : $('#explorerCategories2'),
        $contentHolder : $j('#categoryContent2')
    }
    myExplorer.init(settings);

}

But this simply overwrites the config vales for the first instance which effectively breaks it. I'm beginning to realise that the object literal pattern isn't the way to go here but I'm not sure what is. Can anyone offer any pointers?

A: 

When you call $.extend() like this, it merges the second object's properties into the first:

$.extend(myExplorer.config, settings);

Instead, create a new object that's the result of the merge leaving the first object (the defaults) untouched, like this:

this.settings = $.extend({}, myExplorer.config, settings);

What this does is still merge the passed objects into the first, but the first is a new object ({}), so we're not affecting either of the others, then just use this.settings (or a different name to make it absolutely clear) inside your object.

Nick Craver
Thanks. I tried this out but it didn't work for me. While it created a new settings object within the myExplorer object, the values were still overwritten by the second myExplorer.init(settings) call.
ben
@ben - I wasn't even looking at your object structure, you only have a single object, you'll need to go a completely different route for your object creation using the above, Anurag's answer is one possibility, there are many patterns here.
Nick Craver
A: 

Use a function instead on an object literal, so you can instantiate multiple objects of the widget using the new keyword.

function myExplorer(settings) {
    // init code here, this refers to the current object
    // we're not using a global object like myWindow anymore
    this.config = {
        $wrapper : $('#explorerCategories'),
        $contentHolder : $j('#categoryContent'),
        ..
    };

    // provide for custom configuration
    if (settings && typeof(settings) == 'object') {
        $.extend(this.config, settings);
    }

    this.someFunction = function() { 
        ..
    };

    this.otherFunction = function() {

    };
}

Instantate as many objects of this widget as needed using,

var foo = new myExplorer({ .. });
var bar = new myExplorer({ .. });
...
Anurag
Forgive my ignorance, but is it right that when using this pattern I have to declare all the internal functions first before using them within the surrounding myExplorer function. I just got a bit confused by your //init code here comment.
ben