J86
J86

Reputation: 15237

Knockout Observable array not being observed?

Let me start with a screencast of the current behaviour. As you can see, when I add a role for the user jbrown, the role doesn't show where it should!

But if I navigate away to another user, then back to jbrown, the role(s) does show!

Here is the ViewModel in question:

function UsersViewModel() {
  var self = this;
  // data with some removed for brevity
  self.isLoaded = ko.observable(false);
  self.selectedUser = ko.observable();
  self.SelectedUserHasRoles = ko.computed(function () {
    if (self.isLoaded()) {
      return self.selectedUser().roles().length > 0;
    }
    return false;
  });

  self.UserHasAllAvailableRoles = ko.computed(function () {
    if (self.isLoaded()) {
      return self.userAvailableRoles().length === 0;
    }
    return false;
  });

  // isLoaded gets set to true when AJAX call to server
  // is a success and data is returned

  // operations with some removed for brevity
  self.setCurrentUser = function (user) {
    const newRolesArray = self.roles().filter(function (role) {
      return !contains(user.roles(), role);
    });
    self.userAvailableRoles(newRolesArray);
    self.selectedUser(user);
  }

  self.addUser = function () {
    self.users.push(new User({ Username: this.newUsernameText() }));
    self.newUsernameText('');
    self.NewUserFormIsVisible(false);
    self.AddRolesFormIsVisible(true);
    self.setCurrentUser(self.users()[self.users().length - 1]);
  };

  self.addRoleForSelectedUser = function() {
    self.selectedUser().roles().push(new Role({ Name: self.selectedRole().name() }));
    self.AddRolesFormIsVisible(false);
    $.post('http://localhost:23926/admin/acl/addusertorole', { username: self.selectedUser().name(), role: self.selectedRole().name() },
    function () {
      console.log('user role created at db!');
    },
    'json');
  }
}

And here is the HTML markup in question:

<!-- ko if: isLoaded -->
  <!-- ko if: SelectedUserHasRoles() -->
  <div class="roles-wrapper" data-bind="foreach: $root.selectedUser().roles()">
    <div class="role-token" data-bind="text: name()"></div>
  </div>
  <!-- /ko -->
<!-- /ko -->

My Observable array of roles must be getting populated because as you saw, when I navigate away and back, I can see the roles I've added.

What am I missing?

Upvotes: 0

Views: 31

Answers (2)

Roy J
Roy J

Reputation: 43881

Instead of

self.selectedUser().roles().push(

do

self.selectedUser().roles.push(

That will use the ObservableArray member function push rather than the underlying array's push. The difference is that Knockout only notices the changes made through its member functions.

Upvotes: 1

Jason Spake
Jason Spake

Reputation: 4304

You're trying to push the new role to the underlying, untracked, array instead of the observable array.

self.addRoleForSelectedUser = function() {
    self.selectedUser().roles().push(new Role({ Name: self.selectedRole().name() }));
    ...
}

You just need to remove the set of parenthesis after "roles" so that it looks like:

self.selectedUser().roles.push(new Role({ Name: self.selectedRole().name() }));

Upvotes: 1

Related Questions