2

I am having an insane time trying to understand why I cannot pass an object as an argument with success. When I do, well, weird stuff is happening. When I pass the object instead as a literal, it works fine.

I am trying to build a table by supplying a layout and a dataset, and merging those together into an object. It's working, except that it is setting all data of the rows to be the same. I've found that this only happens when I pass my template as an argument.

My desired result

First Row - First Col
First Row - Sec Col
Sec Row - First Col
Sec Row - Sec Col

My actual result

Sec Row - First Col
Sec Row - Sec Col
Sec Row - First Col
Sec Row - Sec Col

What doesn't work:

var data= {
    0 : {
        col1 : 'First Row - First Col'  ,
        col2 : 'First Row - Sec Col'
    },
    1 : {
        col1 : 'Sec Row - First Col',  
        col2 : 'Sec Row - Sec Col'      
    }
}
var template= {
    col1 : {
        attr : {id : '44'},
        css : {width : '150px'},
        html : ''
    },
    col2 : {
        attr : {id : 1},
        css : {width : '150px'},
        html : ''
    }
}

/**
 * Merge the template and the data
 * @param {Object} template - The table's template
 * @param {Object} data - The table's data
 * @returns {Object} tableData - The merged template and data
 */

Table.prototype.dataToTable = function(template, data){
    var tableData = {};
    var rowCount = Object.keys(data).length;
    for(i = 0; i < rowCount; i++){
        // Here is the issue. If I use = template
        // then both rows are somehow the same. But 
        // if I inject my layout object directly, it's fine!
        tableData[i] = template;
        $.each(tableData[i], function(k,v){
           v.html = data[i][k];
        });
    }
    return tableData;   
}

What does work

Table.prototype.dataToTable = function(template, data){
    var tableData = {};
    var rowCount = Object.keys(data).length;
    for(i = 0; i < rowCount; i++){
        tableData[i] = {
            col1 : {
                attr : {id : '44'},
                css : {width : '150px'},
                html : ''
            },
            col2 : {
                attr : {id : 1},
                css : {width : '150px'},
                html : ''
            }
        };
        $.each(tableData[i], function(k,v){
           v.html = data[i][k];
        });
    }
    return tableData;   
}

http://jsfiddle.net/egea7gf7/9/

forty2011111
  • 115
  • 7
  • 2
    Objects are passed by reference. Within your `$.each` loop, you're actually changing the values in `template`. See this post regarding object cloning ~ http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object. You can probably get around it with `tableData[i] = $.extend(true, {}, template)` – Phil Mar 04 '15 at 05:07
  • Thanks, you should post as an answer – tdoakiiii Mar 04 '15 at 13:56

1 Answers1

1

the problem is because objects in Javascript are transfer only by reference, so when you do oneObject = secondObject, oneObject and secondObject reference will be the same. when you change one you change the other. What you need is to clone your template object.

var clone = function () {
    var cloneFn = function () {
        var newObj = (this instanceof Array ? [] : {});

        for (var index in this) {
            if (this.hasOwnProperty(index)) {
                if (index == 'clone')
                    continue;
                if (this[index] && typeof this[index] == 'object')
                    newObj[index] = cloneFn.apply(this[index]);
                else {
                    if (typeof (this[index]) == 'string')
                        newObj[index] = this[index];
                    else
                        newObj[index] = this[index];
                }
            }
        }
        return newObj;
    };
    return cloneFn.apply(this);
};

tableData[i] = clone.apply(template);

Or something similar Have fun and many smiles

Itay Merchav
  • 954
  • 8
  • 8