2

Can anyone advise me how to create a method call using a string and without using eval? Please note that methodCall cannot be hard-coded and has to be dynamic. methodCall will be created dynamically.

In the following example this refers to a Backbone view, e.g.

var amount = this.model.get('amount');

var $amount = this.$el.find('.amount');

var methodCall = 'this.parentController.parentController.addBinding';

//then need to call the method with args

methodCall('amount',$amount);

I first thought this would work:

this['controller']['parentController']['view']['addBinding'](amount, $amount);

However I came to realise that this would not be dynamic either. Does anyone have a solution?

8
  • 2
    There is nothing wrong with eval as long as you can be sure about what the string you are evaluating contains. Commented Dec 16, 2013 at 8:58
  • 1
    Are you always sure you have to apply it everytime to this? Commented Dec 16, 2013 at 9:05
  • I am in middle of creating a framework which is based on Marionette & Backbone. Also I am using Stickit for two way data binding. System architecture supports child views to access parent model attributes. In view layer developer can call like parent.amount which will bind data to parent model of the particular view. Likewise user should be able to access like patent.parent.amount. I am coming from Angular background and badly missing $scope and Prototypal inheritance. In above example this is refer to a backbone view. Commented Dec 16, 2013 at 9:38
  • @fernando: is this always the top object? Commented Dec 16, 2013 at 15:17
  • @Qantas94Heavy Yes. Always reference start from current backbone view. Therefore this will be always the the top object. Commented Dec 16, 2013 at 20:01

1 Answer 1

2

As noted in this answer, Multiple level attribute retrieval using array notation from a JSON object, you can traverse the hierarchy of objects with something like this:

function findprop(obj, path) {
    var args = path.split('.'), i, l;

    for (i=0, l=args.length; i<l; i++) {
        if (!obj.hasOwnProperty(args[i]))
            return;
        obj = obj[args[i]];
    }

    return obj;
}

You could then give your view/model/collection a method to apply an arbitrary path:

var V = Backbone.View.extend({
    dyncall: function(path) {
        var f = findprop(this, path);
        if (_.isFunction(f))
            return f.call(this, 'your args');
    }
});

var v = new V();
v.dyncall('parentController.parentController.addBinding');

And a demo http://jsfiddle.net/nikoshr/RweEC/

A little more flexibility on passing the arguments :

var V = Backbone.View.extend({
    dyncall: function() {       
        var f = findprop(this, arguments[0]);
        if (_.isFunction(f))
            f.apply(this, _.rest(arguments, 1));
    }
});

var v = new V();
v.dyncall('parentController.parentController.addBinding', 'your arguments', 'another arg');

http://jsfiddle.net/nikoshr/RweEC/1/

Sign up to request clarification or add additional context in comments.

2 Comments

What about properties with . in them, e.g. random['.kkkk']? Still, nice answer :)
@Qantas94Heavy Use a different separator when building the path, something like parentController!parentController!addBinding for example and modify findprop accordingly

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.