【问题标题】:Typescript wrong context this打字稿错误的上下文
【发布时间】:2012-09-27 20:12:41
【问题描述】:

代码:

export class ViewModel {
        public users: knockout.koObservableArrayBase;

        constructor () {
            this.users = ko.observableArray([]);
            this.removeUser = this.removeUser.bind(this);//<-- Here compiller shows error
        }

        removeUser(user: User): void {
            this.users.remove(user);
        }
}

HTML:

<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Surname</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: users">
        <tr>
            <td><a href="#" data-bind="click: $root.removeUser">Remove</a></td>
            <td data-bind="text: name"></td>
            <td data-bind="text: surname"></td>
        </tr>
    </tbody>
</table>

问题出在 removeUser 方法中。默认情况下,如果我不绑定上下文,则 this == UserToDelete - 不是 viewModel 对象。如果我添加到构造函数:this.removeUser = this.removeUser.bind(this); (manually enforce context),那么上下文是需要的 this == viewmodel,但是 TypeScript 会抱怨“无法将函数转换为 (user:User)=>void 需要调用签名,但函数缺少调用签名。”

【问题讨论】:

  • 如果您不介意混乱的 HTML,这是一个替代方案:删除

标签: knockout.js typescript


【解决方案1】:

我不熟悉 ko,所以也许有更好的方法来解决上下文切换,但是您的 typescript 编译器错误是由“bind”返回类型“Function”引起的,该类型与“removeUser”的类型不兼容。您应该能够通过将返回的函数转换为原始类型签名来解决此问题,如下所示:

this.removeUser = <(user: User) => void> this.removeUser.bind(this);

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,这就是为什么我想出了以下基类来解决我的问题

    export class ViewModelBase {
        private prefix: string = 'On';
    
        public Initialize() {
            for (var methodName in this) {
                var fn = this[methodName];
                var newMethodName = methodName.substr(this.prefix.length);
                if (typeof fn === 'function' && methodName.indexOf(this.prefix) == 0 && this[newMethodName] == undefined) {
                    this[newMethodName] = $.proxy(fn, this);
                }
            }
        }
    }
    

    它的作用是循环你类的所有成员,如果一个方法以 On 开头,它将创建一个没有 On 的新方法,它将调用具有正确上下文的原始方法。

    不是$.proxy 是一个 jquery 调用,所以需要 jquery 才能工作。

    【讨论】:

      【解决方案3】:

      嗯,最简单的解决方案以及我通常对 typescript 和 knockout js 所做的事情是,我不在原型上声明从敲除调用的函数,而是在构造函数中声明。所以,我会这样:

      export class ViewModel {
              public users: knockout.koObservableArrayBase;
              removeUser:(user: User) => void;
      
              constructor () {
                  this.users = ko.observableArray([]);
                  this.removeUser = (user:User) => {
                      this.users.remove(user);
                  }
              }
      }
      

      【讨论】:

        【解决方案4】:

        另一种方法是更改​​点击绑定以使用 JavaScript 的 bind 函数强制将 this 的值作为您的视图模型:data-bind="click: $root.MyFunc.bind($root)"

        请注意,$data 和单击 event 对象仍将作为参数从 Knockout 传递给 MyFunc,如 click binding 规范所述。如果您需要覆盖传递给MyFunc 的参数,只需将它们传递给$root 之后的绑定函数,如下所示:.bind($root, param1, param2)。从技术上讲,这些参数将添加到由 Knockout 提供的参数,提供参数[param1, param2, data, event]

        【讨论】:

          【解决方案5】:

          我遇到了同样的问题。要获得正确的上下文,您可以使用点击绑定传递的参数。 点击绑定传递2个参数,即点击的用户和jquery事件。

          如果你使用 jquery 事件,而不是使用 ko.contextFor() 函数,你可以获得正确的上下文。

          你的函数看起来像:

          removeUser(user: User, clickEvent: any): void {
              var self = ko.contextFor(clickEvent.srcElement).$root;
              self.users.remove(user);
          }
          

          【讨论】:

          • 这对我来说是正确的解决方案,但是我目前没有使用 JQuery,所以我只是将 clickEvent.srcElement 更改为 clickEvent.target。谢谢@Paul0515!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-03-23
          • 1970-01-01
          • 1970-01-01
          • 2023-01-20
          • 2020-12-19
          • 2023-03-29
          • 1970-01-01
          相关资源
          最近更新 更多