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?

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
fernando
  • 707
  • 8
  • 24
  • 2
    There is nothing wrong with `eval` as long as you can be sure about what the string you are evaluating contains. – The Internet Dec 16 '13 at 08:58
  • 1
    Are you always sure you have to apply it everytime to this? – MarcoL Dec 16 '13 at 09: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. – fernando Dec 16 '13 at 09:38
  • @fernando: is `this` always the top object? – Qantas 94 Heavy Dec 16 '13 at 15:17
  • @Qantas94Heavy Yes. Always reference start from current backbone view. Therefore `this` will be always the the top object. – fernando Dec 16 '13 at 20:01
  • Are you not able to use `methodCall = this['parentController.parentController.addBinding']`? – fbynite Dec 16 '13 at 20:20
  • @fbynite: that is different to `this.parentController.parentController.addBinding` though. – Qantas 94 Heavy Dec 17 '13 at 00:04
  • As long as you check your string before passing it to `eval`, there's no need to worry about `eval` security issues. – Qantas 94 Heavy Dec 17 '13 at 12:58

1 Answers1

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/

Community
  • 1
  • 1
nikoshr
  • 32,926
  • 33
  • 91
  • 105
  • What about properties with `.` in them, e.g. `random['.kkkk']`? Still, nice answer :) – Qantas 94 Heavy Dec 17 '13 at 13:10
  • 1
    @Qantas94Heavy Use a different separator when building the path, something like `parentController!parentController!addBinding` for example and modify `findprop` accordingly – nikoshr Dec 17 '13 at 13:21