Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"this" issue when using viewmodel in typescript and knockout

See below for updated code

I'm trying to use knockout with typescript but it seems I'm not able to bind my typescript viewmodel to my view... it seems I have an issue with the this but I don't understand why...

The this.UserList is undefined when calling the Fill method

Here is my 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);
        }


    }
}

Here is my View:

<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>

}

Here is my model:

 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>;
     }
 }

New working code for helping purposed:

ViewModel:

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);
        }

    }
}

Model:

 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>;
     }
 }

View:

<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>

}
like image 305
Hemadeus Avatar asked Dec 01 '22 02:12

Hemadeus


1 Answers

If you want this to be bound to the UserViewModel that then maybe use this approach:

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);
        }
    }
}

Here's a TypeScript Playground to demo

You can read an explanation of the approach being used here: http://blogs.msdn.com/b/typescript/archive/2013/08/06/announcing-0-9-1.aspx (check "Better 'this' handling)

By the way, I suspect where you have KnockoutObservable<Models.User> you should actually have Models.User and when you have this.UserList.push(ko.observable(guest)); you actually should have this.UserList.push(guest);. I haven't changed the code sample in case I missed something.

like image 80
John Reilly Avatar answered Dec 04 '22 08:12

John Reilly