Search code examples
javascriptjqueryjsonformsbackbone.js

Trouble overriding toString on object/class


As the title says, I'm trying to implement some conditional display logic to my form built using backbone-forms. The environment also uses Backbone.js, Underscore.js and jQuery.

I have reviewed the similar question here on SO "backbone-forms with conditional fields", but our use case is a bit different: we have fields that depend on multiple conditions (e.g. only display if foo=1 and bar=2).

[edit: I've already written a generic form change event handler to handle hiding and showing the different fields, so I don't want suggestions about that part.]

I'm trying to use data attributes to contain a JSON representation of the field's conditions, but I'm open to other solutions. Here's roughly what I'm currently trying to achieve:

<input id="field3" name="field3" data-conditional-conditions='{"field1":"yes","field2":"yes"}' type="text">

I've discovered that backbone-forms uses jQuery's attr() method to set any properties specified in the model's schema (here's the relevant line in backbone-forms' source), and it seems that attr() in turn uses Object.prototype.toString() to represent an object as a value when rendering the editor.*

So what I've been trying to do is implement a Conditions object as follows:

var Conditions = function(args){

    c = {
        toString: function(){
            return(JSON.stringify( _.omit(this, _.functions(this)) ));
        }
    };

    c.toString = _.bind(c.toString, c); //makes 'this' refer to the 'c' object

    args = _.isObject(args) ? args : {}; //default value for args

    c = _.defaults(c, args); //sets object key/value pairs from arguments

    return c;
};

My goal with the above was to replace the toString() method with one that would return a JSON-formatted representation of the object, without having to modify backbone-forms.

Unfortunately, the above doesn't quite work, as the form field appears as follows:

<input id="field3" name="field3" tostring="{"data-conditional-conditions":{"field1":"yes","field2":"yes"}}" data-conditional-conditions="[object Object]" type="text">

I'm reaching the end of my JavaScript knowledge, and frustrated by what's going on. I've tried using IIFEs and returning c.toString from my Conditions function, but neither of those have worked.

Thanks for reading, and I look forward to your suggestions!

*: I've inferred this because if I call attr({"data-key": {"foo": "bar"} }), the result looks like <div data-key="[object Object]">


Solution

  • So, it turns out I was pretty close, but just needed to do things a bit differently.

    What I ended up doing to solve it was

    • iterate over every key, value of the args object parameter (which I get from arguments[0] in this case)
    • detect if value is an object
    • if so, assign the value of args[key] to an IIFE that returns a JSON representation of the object value.

    The IIFE turns out to be necessary because it maintains the value of value even when we change scope.

    Here's my completed and working Conditions class:

    var Conditions = function(){
    
        var args = arguments[0];
    
        //if we didn't call Conditions() with 'new', this next bit handles that
        if (!(this instanceof Conditions)){
            return new Conditions(args);
        }
    
        //default value for 'args' if we don't have an arguments object
        if (!_.isObject(args)){
            args = {};
        }
    
        _.each(args, function(val, key, obj){
            //detect where value is object
            if (_.isObject(val)){
                //replace these values with objectToJSON(value)
                args[key] = (function(obj){ return(JSON.stringify(obj)) })(val);
            }
        });
    
        return args;
    };