简体   繁体   中英

Update an array item in knockout js

I have an array of "users" in a KnockoutJS observable array. I can add users to the array and it updates the view correctly, but the editing of users is currently driving me nuts, probably because of a slight lack of understanding of the base concept on my part.

View Model

var viewModel = {
    users: ko.observableArray(),
    user: ko.observable(),
    showEditForm: function (model) {
        if (!$('#users-form').is(':visible')) {
            $('#mask').show();
        }
        showUsersLoading();
        loadUserIntoEditForm(model.Id);
    },
    getUser: function (userId) {
        for(var i = 0; i < this.users().length; ++i)
        {
            if (this.users()[i].Id === userId)
            {
                this.user(this.users()[i]);
                break;
            }
        }
    }
};

User View Model (this is primarily used for the add functionality at the moment)

   var userViewModel = function (id, username, statusDescription, email) {
        var self = this;
        self.Id = ko.observable(id),
        self.Name = ko.observable(username),
        self.StatusDescription = ko.observable(statusDescription),
        self.Email = ko.observable(email)
    };

The updating / editing is performed in an MVC partial view that fires off an ajax request to update the user server side, then on a successful response, runs the following code to update the user

viewModel.getUser(result.Id);
viewModel.user().StatusDescription('locked');
viewModel.user().Name('testingUpdate');

Which gives me a Uncaught TypeError: string is not a function error

This is where my understanding fails me. I get that the user that I've grabbed from the users array doesn't have observable properties, which is why I can't update using the Knockout function method, I've confirmed this by pulling out details of the users array in the browser console window.

I also know that, conceptually, I want to cast the user observable object to a userViewModel object so the properties then become observable and I can update them; or I want the users observable array to know that the objects it contains should be of type userViewModel, so the object properties are observable.

The problem I'm having is although I understand the concept, I can't figure out the code to actually make it work.

The problem is the this keyword. In your sample it's not referring to what you expect.

Try using the RMP (revealing module pattern) to simplify the way you write your code. It looks something like this:

var viewModel = (function() {
    var users = ko.observableArray();
    var user = ko.observable();

    // This one changes: you can use the vars directly

    var getUser = function (userId) {
        for(var i = 0; i < users().length; ++i)
        {
            if (users()[i].Id === userId)
            {
                user(this.users());
                break;
            }
        }
    }

    // Reveal the data

    return {
        users: users,
        user: user,
        getUser : getUser
    };

})();  // self-executing anonymous function 

The anonymous function puts a closure around your vars. You can use the vars safely inside that closure, and they are not available outside of it. Besides you can have "private vars" by simply don't revealing them.

Additional note: I recommend you to use lodash or underscore to simplify array manipulation: you'd avoid writing loops to find an element in an array. For example using lodash find .

As always, the easy answer is, I'm an idiot.

The key part that I was missing is forgetting that I'm not dealing with strongly typed objects, and assuming that they'll just work the way I expect.

The success callback for my ajax call to populate the users() array in the viewModel object used to be

success: function (data) {
    viewModel.users(data)
    setupDatatable();
}

Which, looking back at it, the problem is obvious. I'm inserting generic objects into the array, not userViewModel objects.

So, when I changed it to:

success: function (data) {
    for (var i = 0; i < data.length; ++i) {
        viewModel.users.push(new userViewModel(data[i].Id, data[i].Name, data[i].StatusDescription, data[i].Email));
    }
    setupDatatable();
}

Suddenly it started working.

Hopefully, my stupidity and days worth of headaches will help someone else :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM