andrsnn
andrsnn

Reputation: 1621

javascript chainable nested subclass

I'm effectively trying to build a chainable object that contains sub objects or other functions.

It should be used like this:

val().rules.phone("someValue");

So far:

var val = function(){
  var validator = function(){
    this.fields = [];
    return this;
  }
  validator.prototype = {
    addField: function(fieldName){
       this.fields.push(fieldName);
       return this;
    },
    rules: {
      phone: function(){
        //RETURNING THIS DOES NOT RETURN THE PARENT SCOPE
        //IT RETURNS SCOPE OF PHONE FUNCTION
        return this;
      }
    }
  }
  return new validator();

}

I also tried a circular reference via prototype chaining:

var val = function(){
    var validator = function(){
        this.fields = [];

        return this;
    }

    var rules = function(){
        validator.call(this);
    }
    rules.prototype = Object.create(validator.prototype);
    rules.prototype.constructor = rules;
    rules.prototype.phone = function(){
    console.log('hone');   
    }

    validator.prototype = {
        addField: function(fieldName){
            this.fields.push(fieldName);    
            return this;
        },
        rules: new rules()

    }

   return new validator();
}

var z = val().rules;
//no 'addFields' function exists on the validator object in the chain.
console.log(z);

Problems:

In the first val().rules.phone() returns the scope of its function rather then the parent scope. It should return the validator scope.

In the second example, val().rules DOES have validator as parent on its prototype chain, however addFields prints undefined, and does not exist in console when tracing the chain.

I know I am over complicating the issue by adding the rules literal. I could just implement chaining with functions directly placed on the prototype object. This is for my own clarification. Is there any other possible way to implement this with the same style usage?

Upvotes: 0

Views: 64

Answers (2)

Andrew Magee
Andrew Magee

Reputation: 6684

You can get an API like the one you want with this:

var validator = function(){
    this.fields = [];
    this.rules = new rules(this);
}
validator.prototype = {
    addField: function(fieldName){
        this.fields.push(fieldName);    
        return this;
    }
};

var rules = function(validator) {
  this.validator = validator;
}
rules.prototype = {
  addField: function(fieldName) {
    this.validator.addField(fieldName);
    return this;
  },
  phone: function() {
    console.log("phone");
  }
};

v = new validator();
v.rules.addField("hello");
v.rules.phone();

Making rules inherit validator is semantically and practically problematic. Your validator.prototype here:

validator.prototype = {
    addField: function(fieldName){
        this.fields.push(fieldName);    
        return this;
    },
    rules: new rules()
}

will share the same rules object between all validator objects, which is probably not what you want. But then if you fixed this, by making a rules object in the validator constructor, you would have an infinite loop, since the validator constructor would call the rules constructor, and the rules constructor would call the validator constructor (since a rules is a validator).

Upvotes: 1

mohamedrias
mohamedrias

Reputation: 18566

var val = function(){
    var validator = function(){
        this.fields = [];

        return this;
    }

    var rules = function(){
        if(!this instanceof rules) return new rules();
        validator.call(this);
        return this;
    }
    validator.prototype = {
        addField: function(fieldName){
            this.fields.push(fieldName);    
            return this;
        },
        rules: rules

    }
    rules.prototype = Object.create(validator.prototype);
    rules.prototype.phone = function(){
    console.log('hone');   
    }



   return new validator();
}

z = val().rules actually returns you an object and not a function. that's why you are unable to use prototype for that.

Upvotes: 1

Related Questions