在typescript和knockout中使用viewmodel时出现“this”问题

时间:2014-01-08 18:34:20

标签: asp.net-mvc knockout.js typescript

请参阅下面的更新代码

我正在尝试使用打字稿和打字稿,但似乎我无法将我的打字稿视图模型绑定到我的视图......似乎我有this的问题,但我不明白为什么...

调用this.UserList方法

时,Fill未定义

这是我的viewModel:

module ViewModels {
    export class UserViewModel {

        constructor() {
            this.UserList = ko.observableArray<KnockoutObservable<Models.User>>([]);

            this.RemoveItem = <(user: KnockoutObservable<Models.User>) => void> this.RemoveItem.bind(this);
            this.AcceptItem = <(user: Models.User) => void> this.AcceptItem.bind(this);
            this.AddItem = <() => void> this.AddItem.bind(this);
        }

        public UserList: KnockoutObservableArray<KnockoutObservable<Models.User>>;
        public Fill() {
            $.ajax({
                type: "POST",
                url: "/Guest/LoadGuest/",
                success: data=> {
                    $(data).each((index: any, item)=> {
                        var guest = new Models.User();
                        guest.FirstName = ko.observable(item.firstName);
                        guest.LastName = ko.observable(item.lastName);
                        guest.IsNew = ko.observable(false);
                        this.UserList.push(ko.observable(guest));
                    });
                }
            });
        }

        public AddItem() {
            var guest = new Models.User();
            guest.FirstName = ko.observable("");
            guest.LastName = ko.observable("");
            guest.IsNew = ko.observable(true);

            this.UserList.push(ko.observable(guest));
        }

        public AcceptItem(user: Models.User) {
            user.IsNew = ko.observable(false);
        }


        public RemoveItem(user: KnockoutObservable<Models.User>) {
            this.UserList.remove(user);
        }


    }
}

这是我的观点:

<table>
    <thead>
        <tr><th>Prénom</th><th>Nom de famille</th></tr>
    </thead>
    <tbody data-bind="foreach: UserList">
        <tr data-bind="if:IsNew">
            <td><input type="text" data-bind="text: FirstName" /></td>
            <td><input type="text" data-bind="text: LastName" /></td>
            <td><a data-bind="click:$parent.AcceptItem">OK</a><a data-bind="click:$parent.RemoveItem">Annuler</a></td>
        </tr>
        <tr data-bind="if:!IsNew">
            <td data-bind="text: FirstName"></td>
            <td data-bind="text: LastName"></td>
            <td></td>
        </tr>
    </tbody>
</table>
<a id="AddGuest" data-bind="click:AddItem">Add</a>

@section scripts{
    <script src="~/Scripts/models/ModelBase.js"></script>
    <script src="~/Scripts/models/User.js"></script>
    <script src="~/Scripts/viewmodels/UserViewModel.js"></script>
    <script>

        var vm = new ViewModels.UserViewModel();
        vm.Fill();
        ko.applyBindings(vm);
    </script>

}

这是我的模特:

 module Models{

     export class User extends Models.ModelBase {
         constructor() {
             super();

         }

         public FirstName: KnockoutObservable<string>;
         public LastName: KnockoutObservable<string>;
         public Age: KnockoutObservable<Age>;
     }

     export class Age {
         public ID: KnockoutObservable<number>;
         public Description: KnockoutObservable<string>;
     }
 }

帮助改善目标的新工作代码:

视图模型:

module ViewModels {
    export class UserViewModel {
        constructor() {

        }

        public UserList: KnockoutObservableArray<Models.User> = ko.observableArray<Models.User>([]);
        public Fill = () => {
            $.ajax({
                type: "POST",
                url: "/Guest/LoadGuest/",
                success: data=> {
                    for (var i = 0; i < data.length; i++) {
                        var item = data[i];
                        var guest = new Models.User();
                        guest.FirstName(item.firstName);
                        guest.LastName(item.lastName);
                        guest.Age().Description("Test Age");
                        guest.IsNew(false);
                        this.UserList.push(guest);
                    }
                }
            });
        }

        public AddItem = () => {
            var guest = new Models.User();
            guest.FirstName = ko.observable("");
            guest.LastName = ko.observable("");
            guest.IsNew(true);
            this.UserList.push(guest);

        }

        public AcceptItem = (user: Models.User) => {
            user.IsNew(false);
        }

        public RemoveItem = (user: Models.User)  => {
            this.UserList.remove(user);
        }

    }
}

型号:

 module Models{

    export class ModelBase{
        constructor() {
            this.IsNew = ko.observable(false);
        }
        public IsNew: KnockoutObservable<boolean>;
    }

     export interface IUser {
         FirstName: KnockoutObservable<string>;
         LastName: KnockoutObservable<string>;
         Age: KnockoutObservable<Age>;
     }


     export class User extends Models.ModelBase implements IUser {
         constructor() {
             super();
             this.FirstName = ko.observable("");
             this.LastName = ko.observable("");
             this.Age = ko.observable(new Age());
         }

         public FirstName: KnockoutObservable<string>;
         public LastName: KnockoutObservable<string>;
         public Age: KnockoutObservable<Age>;
     }

     export class Age {
         constructor() {
             this.ID = ko.observable(null);
             this.Description = ko.observable("");
         }

         public ID: KnockoutObservable<number>;
         public Description: KnockoutObservable<string>;
     }
 }

查看:

<table>
        <thead>
            <tr><th>Prénom</th><th>Nom de famille</th><th>Age</th></tr>
        </thead>
        <tbody data-bind="foreach: UserList">
            <tr data-bind="ifnot:$data.IsNew">
                <td data-bind="text: FirstName"></td>
                <td data-bind="text: LastName"></td>
                <td data-bind="text: Age().Description"></td>
            </tr>
            <tr data-bind="if:$data.IsNew">
                <td><input type="text" data-bind="value: FirstName" /></td>
                <td><input type="text" data-bind="value: LastName" /></td>
                <td><input type="text" data-bind="value: Age().Description" /></td>
                <td><a data-bind="click:$root.AcceptItem">OK</a><a data-bind="click:$root.RemoveItem">Annuler</a></td>
            </tr>
        </tbody>
</table>


    <a id="AddGuest" data-bind="click:AddItem">Add</a>
@section scripts{
    <script src="~/Scripts/models/ModelBase.js"></script>
    <script src="~/Scripts/models/User.js"></script>
    <script src="~/Scripts/viewmodels/UserViewModel.js"></script>
    <script>
        var vm = new ViewModels.UserViewModel();
        vm.Fill();
        ko.applyBindings(vm);
    </script>

}

5 个答案:

答案 0 :(得分:9)

如果您希望将this绑定到UserViewModel,那么可以使用此方法:

module ViewModels {
    export class UserViewModel {

        UserList: KnockoutObservableArray<KnockoutObservable<Models.User>> = ko.observableArray<KnockoutObservable<Models.User>>([]);

        Fill = () => {
            $.ajax({
                type: "POST",
                url: "/Guest/LoadGuest/",
                success: data => {
                    $(data).each((index: any, item)=> {
                        var guest = new Models.User();
                        guest.FirstName = ko.observable(item.firstName);
                        guest.LastName = ko.observable(item.lastName);
                        guest.IsNew = ko.observable(false);
                        this.UserList.push(ko.observable(guest));
                    });
                }
            });
        }

        AddItem = () => {
            var guest = new Models.User();
            guest.FirstName = ko.observable("");
            guest.LastName = ko.observable("");
            guest.IsNew = ko.observable(true);

            this.UserList.push(ko.observable(guest));
        }

        AcceptItem = (user: Models.User) => {
            user.IsNew = ko.observable(false);
        }


        RemoveItem = (user: KnockoutObservable<Models.User>) => {
            this.UserList.remove(user);
        }
    }
}

这是demo

的TypeScript Playground

您可以阅读此处使用的方法的说明:http://blogs.msdn.com/b/typescript/archive/2013/08/06/announcing-0-9-1.aspx(选中“更好'此'处理方式)

顺便说一句,我怀疑你有KnockoutObservable<Models.User>你应该拥有Models.User的位置,当你有this.UserList.push(ko.observable(guest));时,你实际应该拥有this.UserList.push(guest);。我没有更改代码示例,以防我错过了什么。

答案 1 :(得分:6)

此处的其他答案假设您对问题的描述是准确的。我决定实际测试你的代码。这是我发现的:

  1. 错误的each功能。

    $(data).each((index: any, item)=> {
    

    这可能在技术上有效,但它应该是针对DOM节点的。您应该使用$.each

    $.each(data, (index: any, item)=> {
    
  2. 在可观察的视图中包装视图模型对象。

    this.UserList.push(ko.observable(guest));
    

    这会破坏您的RemoveItem功能。你应该只添加对象。

    this.UserList.push(guest);
    
  3. 覆盖一个observable而不是设置它。

    user.IsNew = ko.observable(false);
    

    Knockout将无法在此处看到更改。您需要设置observable。

    user.IsNew(false);
    
  4. <input>元素使用错误的绑定。

    <td><input type="text" data-bind="text: FirstName" /></td>
    

    text绑定是单向的。您需要使用value

    <td><input type="text" data-bind="value: FirstName" /></td>
    
  5. 在表达式中不正确地使用observable。

    <tr data-bind="if:!IsNew">
    

    这测试observable本身是false,它始终不是(因为它是一个函数)。您想测试observable的值是false!IsNew()。更好的是,只需使用ifnot绑定。

    <tr data-bind="ifnot:IsNew">
    

答案 2 :(得分:3)

使用lamda语法。

只需更改此行

即可

public Fill() {

到此:

public Fill = () => {

在我们的代码库中,我发现我几乎不需要诉诸var self = this;解决方法。

其他建议:

1)如果您将功能更改为使用lamda语法,则可以摆脱所有这些业务:

        this.RemoveItem = <(user: KnockoutObservable<Models.User>) => void> this.RemoveItem.bind(this);
        this.AcceptItem = <(user: Models.User) => void> this.AcceptItem.bind(this);
        this.AddItem = <() => void> this.AddItem.bind(this);

2)只需初始化你声明它们的公共属性 - 在构造函数中执行它有很多冗余代码(即与上面相同)

而不是:

public UserList: KnockoutObservableArray<KnockoutObservable<Models.User>>;

这样做:

public UserList = ko.ObservableArray<Modesl.User>([]);

您的UsersList对象仍然是强类型的,您的构造函数中不需要这一行:this.UserList = ko.observableArray<Models.User>([]); 请注意,我同意另一张海报,您可能实际上没有想要一个observable array of observables所以为了清楚起见,我删除了那个古怪的东西。

答案 3 :(得分:1)

原因是当你在填充方法中时,'this'指的是一个新的上下文。为了保持'this'的含义,你可以使用像self这样的变量在树下进一步引用它 -

module ViewModels {
    export class UserViewModel {
        constructor() {
            var self = this;
            self.UserList = ko.observableArray<KnockoutObservable<Models.User>>([]);

            self.RemoveItem = <(user: KnockoutObservable<Models.User>) => void> this.RemoveItem.bind(this);
            self.AcceptItem = <(user: Models.User) => void> this.AcceptItem.bind(this);
            self.AddItem = <() => void> this.AddItem.bind(this);
        }

        public UserList: KnockoutObservableArray<KnockoutObservable<Models.User>>;
        public Fill() {
            var self = this;
            $.ajax({
                type: "POST",
                url: "/Guest/LoadGuest/",
                success: data=> {
                    $(data).each((index: any, item)=> {
                        var guest = new Models.User();
                        guest.FirstName = ko.observable(item.firstName);
                        guest.LastName = ko.observable(item.lastName);
                        guest.IsNew = ko.observable(false);
                        self.UserList.push(ko.observable(guest));
                    });
                }
            });
        }


    }
}

答案 4 :(得分:1)

如果您按照接受的答案的建议将函数签名从MyFunc() { }更改为lambda格式MyFunc = () => { },您会发现您的函数不再是您班级的共享原型函数,而是而是复制为每个类实例的函数变量。

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

请注意,$data和点击event对象仍会作为<{1}}的参数从Knockout传入,如 click binding 所述规格。如果您需要覆盖传递给MyFunc的参数,只需将它们传递到MyFunc之后的绑定函数中,如下所示:$root。从技术上讲,这些参数将预先到Knockout提供的参数,并提供参数.bind($root, param1, param2)