TypeScript and Knockout binding to 'this' issue - lambda function needed?

后端 未结 8 1612
被撕碎了的回忆
被撕碎了的回忆 2020-12-04 18:28

I\'ve been creating a htmlHelper function using TypeScript and KnockoutJS to edit a list of emails.

The list of emails is a Knockout ObservableArray called e

相关标签:
8条回答
  • 2020-12-04 18:34

    to add my 2 cents, there's also a dirty way, that leverages the variable _this created by the Typescript compiler to keep a reference on this :

    public deleteItem(emailToDelete: string) {
        var that = eval('_this');
        // remove item from list
        that.emails.remove(emailToDelete); // remove? in JS,  really? 
    }
    
    0 讨论(0)
  • 2020-12-04 18:37
    declare class Email { }
    declare class ObservableArray {
        remove(any): void;
    }
    
    class MyViewModel {
        public emails : ObservableArray;
    
        constructor() {
            Rebind(this);
        }
    
        public deleteItem(emailToDelete: Email) {
            this.emails.remove(emailToDelete);
        }
    }
    
    function Rebind(obj : any)
    {
        var prototype = <Object>obj.constructor.prototype;
        for (var name in prototype) {
            if (!obj.hasOwnProperty(name)
                    && typeof prototype[name] === "function") {
                var method = <Function>prototype[name];
                obj[name] = method.bind(obj);
            }
        }
    }
    

    You might want a polyfill for Function.bind():

    // Polyfill for Function.bind(). Slightly modified version of
    // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
    if (typeof Function.prototype.bind !== "function") {
        Function.prototype.bind = function(oThis) {
            if (typeof this !== "function") {
                // closest thing possible to the ECMAScript 5 internal IsCallable function
                throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
            }
    
            var aArgs = <any[]> Array.prototype.slice.call(arguments, 1),
                fToBind = this,
                fNOP = function() {},
                fBound = function() {
                    return fToBind.apply(this instanceof fNOP && oThis ? this: oThis, aArgs.concat());
                };
    
            fNOP.prototype = this.prototype;
            fBound.prototype = new fNOP();
    
            return fBound;
        };
    }
    
    0 讨论(0)
  • 2020-12-04 18:42

    My final solution is a base class, that rebinds all prototype functions to itself on constructor. Much like Markus Jarderot's solution.

    class BaseClass {
        constructor() {
            for (var i in this) {
                if (!this.hasOwnProperty(i) && typeof (this[i]) === 'function' && i != 'constructor') {
                    this[i] = this[i].bind(this);
                }
            }
        }
    }
    

    Advantages:

    • All subclasses are forced to call super constructor, which is the behavior I wanted.
    • When the rebind code is executed, there are only prototype functions in the object (variables are added later).
    • It avoids the creation of big functions on every object. Only a small proxy function is created per object when you call bind.
    • Better organization of class code by not putting functions on constructor.
    • Any function can be used as a callback, you don't need to change the code when a function is called from an event.
    • You don't have the risk of binding functions twice.
    • It is better to bind the function only once, instead doing it in the view every time the click/event binding is executed.

    PS:
    You still will need the bind polyfill.
    I'm using typesript 0.9.5

    0 讨论(0)
  • 2020-12-04 18:47

    Use data-bind something like this:

    data-bind="click:$parent.deleteItem.bind($parent)"
    

    Assign this to that as shown below

    public deleteItem(itemToDelete) 
    {
        var that = this;
        // remove item from list
        that.emails.remove(itemToDelete); 
    }
    
    0 讨论(0)
  • 2020-12-04 18:52

    Gloves people! Just bind $parent as this:

    <a href="#" data-bind="click: $parent.deleteItem.bind($parent)">Delete</a>
    
    0 讨论(0)
  • Although I prefer Markus' solution, here's what I have used before to work around this issue:

    public fixThis(_this, func) {
        return function () {
            return _this[func].apply(_this, arguments);
        };
    }
    
    <a href="#" data-bind="click: fixThis($parent, 'deleteItem')">Delete</a>
    

    Note that additional arguments can be passed to the method by adding them after the name of the method:

    fixThis($parent, 'deleteItem', arg1, arg2);
    
    0 讨论(0)
提交回复
热议问题