【问题标题】:Knockout $root context is wrong in foreach binding在 foreach 绑定中敲除 $root 上下文是错误的
【发布时间】:2016-04-30 12:39:15
【问题描述】:

当我使用foreach 数据绑定时,它似乎打破了$root 上下文以进行淘汰赛。

当这种情况发生时,this 指的是一个元素 (user),它是 viewmodel (page) 的子元素和 foreach 元素 (requests) 的父元素,它应该引用 viewmodel (page)。

我在 foreach 之外引用 $root 没有问题 - 它在正确的上下文中调用正确的方法。

适用的 HTML:

<div id="home-page-view" class="view" data-bind="if: status() == AppStatus.Home">
        <button data-bind="click: $root.switchState">Switch To Friend Requests</button>
</div>

Typescript 的作用:

public switchState() {
        if (this.status() == AppStatus.Home) {
            this.status(AppStatus.FriendRequests); //this refers to viewmodel (page)
        } else {
            this.status(AppStatus.Home);
        }
    }

但是,在 foreach 中,像data-bind="click: $root.acceptFriendRequest" 这样绑定的按钮成功调用了它们的方法,但this 指的是user,而不是page

HTML 损坏的地方:

<div id="friend-requests-view" class="view" data-bind="if: status() == AppStatus.FriendRequests">
    <h1>Friend Requests</h1>
    <p>View your pending friend requests.</p>

    <div data-bind="with: user">
        <div data-bind="foreach: requests">
            <div>
                <h2 data-bind="text: firstName"></h2>
                <p data-bind="text: lastName"></p>
            </div>
            <div>
                <input type="button" value="Accept" data-bind="click: $root.acceptFriendRequest" />
                <input type="button" value="Deny" data-bind="click: $root.rejectFriendRequest" />
            </div>
        </div>
    </div>
</div>

打字稿损坏的地方:

public acceptFriendRequest(newFriend: UserModel) {
        this.respondToFriendRequest(true, newFriend);
}

public rejectFriendRequest(notFriend: UserModel) {
        this.respondToFriendRequest(false, notFriend);
}

即使在其损坏的 HTML 中,status() 之前的 4 行隐含地引用了正确的 $root

这里发生了什么导致this 引用page.user 而不是page?鉴于在其他情况下this 指的是page,看来$root 应该总是指回page

MCVE:

Page.html

<!DOCTYPE html>
<head>
    <meta charset="utf-8" />

    <script type="text/javascript" src="/Scripts/jquery-2.1.4.js"></script>
    <script type="text/javascript" src="/Scripts/jquery-ui-1.11.4.js"></script>
    <script type="text/javascript" src="/Scripts/knockout-3.3.0.js"></script>
    <script type="text/javascript" src="/Scripts/knockout.mapping-latest.js"></script>
    <script type="text/javascript" src="app.js"></script>
    <script type="text/javascript" src="DTO/DTOs.js"></script>
</head>

<body>
    <div id="home-page-view" class="view" data-bind="if: status() == AppStatus.Home">
        <button data-bind="click: $root.switchState">Switch To Friend Requests</button>
    </div>

    <div id="friend-requests-view" class="view" data-bind="if: status() == AppStatus.FriendRequests">
        <h1>Friend Requests</h1>
        <p>View your pending friend requests.</p>

        <div data-bind="with: user">
            <div data-bind="foreach: requests">
                <div>
                    <h2 data-bind="text: firstName"></h2>
                    <p data-bind="text: lastName"></p>
                </div>
                <div>
                    <input type="button" value="Accept" data-bind="click: $root.acceptFriendRequest" />
                    <input type="button" value="Deny" data-bind="click: $root.rejectFriendRequest" />
                </div>
            </div>
        </div>
    </div>
</body>
</html>

App.ts

/// <reference path="Scripts/typings/jquery/jquery.d.ts" />
/// <reference path="Scripts/typings/jqueryui/jqueryui.d.ts" />
/// <reference path="Scripts/typings/knockout/knockout.d.ts" />
/// <reference path="Scripts/typings/knockout.mapping/knockout.mapping.d.ts" />


var page: App;

$(document).ready(function () {
    $.ajaxSetup({ cache: false });
    page = new App();
    ko.applyBindings(page);
});


class App {

    public user: KnockoutObservable<UserModel>;
    public status: KnockoutObservable<AppStatus> = ko.observable(AppStatus.Home);

    constructor() {
        this.user = ko.observable(new UserModel());
        var us: User = {firstName: "Chip", lastName: "Dipson" };
        this.user(ko.mapping.fromJS(us));
        this.user().requests.push(new UserModel());
    }

   public switchState() {
        if (this.status() == AppStatus.Home) {
            this.status(AppStatus.FriendRequests);
        } else {
            this.status(AppStatus.Home);
        }
    }

    public acceptFriendRequest(newFriend: UserModel) {
        this.respondToFriendRequest(true, newFriend); //this is wrong
    }

    public rejectFriendRequest(notFriend: UserModel) {
        this.respondToFriendRequest(false, notFriend); //this is wrong
    }

    private respondToFriendRequest(accepted: boolean, requester: UserModel) {
      //do some ajax stuff
    }
}

enum AppStatus {
    Home, FriendRequests
}

DTOs.ts

class User {
    firstName: string;
    lastName: string;

    requests: User[];
}

class UserModel {
    firstName: KnockoutObservable<string> = ko.observable("");
    lastName: KnockoutObservable<string> = ko.observable("");

    requests: KnockoutObservableArray<UserModel> = ko.observableArray<UserModel>(null);
}

【问题讨论】:

    标签: javascript html knockout.js typescript


    【解决方案1】:

    click 绑定采用函数,而不是代码的 sn-p。这意味着 $root 不提供执行上下文,它只是帮助您定位函数。如果你想设置执行上下文,你需要bind它:

    <input type="button" value="Accept" data-bind="click: $root.acceptFriendRequest.bind($root)" />
    

    【讨论】:

    • 就是这样。谢谢!
    猜你喜欢
    • 2015-12-06
    • 2015-05-26
    • 2013-06-08
    • 2012-06-09
    • 2017-08-31
    • 2015-11-14
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多