Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript object literal pattern with multiple instances

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?

like image 942
benb Avatar asked Oct 12 '10 09:10

benb


4 Answers

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({ .. });
...
like image 168
Anurag Avatar answered Nov 01 '22 17:11

Anurag


What about this?

var myExplorer = function(settings) {

  var o = {
    init: function() {
      this.somevalue = settings;
    },

    whatever: function() {
    }
  };

  o.init();

  return o;
};

var exp1 = myExplorer('something');
var exp2 = myExplorer('anything');

console.log(exp1.somevalue); //something
console.log(exp2.somevalue); //anything
like image 40
anonymous_coward Avatar answered Nov 01 '22 16:11

anonymous_coward


Use the following code to achieve this, remember about the 'public API' (so that the 'internal' function will be visible 'outside'):

var myExplorer = function() {
    var init = function(settings) {
        var 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(config, settings);
        }

        // some more code...
    },

    var createExpanderLink = function() {
        // more code
    },

    var anotherMethod = function() {
        // etc
    }

    // Public API 
    // return the functions you want to use outside of the current instance
    return {
        init : init,
        createExpanderLink : createExpanderLink,
        anotherMethod : anotherMethod
    }
}
var firstExplorer = new myExplorer();
var secondExplorer = new myExplorer();
// etc 
like image 4
alessioalex Avatar answered Nov 01 '22 17:11

alessioalex


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.

like image 2
Nick Craver Avatar answered Nov 01 '22 17:11

Nick Craver