3

Can you guys help me debug why when I call replace it is not being reflected upon the UI. The ****** in the checkout function is where I call replace. In the index I am looping through my observableArray to display the items.

Current the UI will on be reflected when I refresh the page.

Thanks!

JS

$(function(){
  ko.applyBindings(new ViewModelBooks());
});


function Book(title, user_id, book_id)
{
  this.title = ko.observable(title);
  this.user_id = ko.observable(user_id);
  this.book_id = ko.observable(book_id);
}

function ViewModelBooks(){
  var self = this;
  self.library = ko.observableArray();
  getBooks();

  // Click event for checking out a book
  self.checkOutBook = function(obj){
    checkOutBook(obj, "Book");
  };


  function checkOutBook(book, list_name)
  {
    // Get current user
    alert(self.library.indexOf(book));
    var user_id = $().SPServices.SPGetCurrentUser({
      fieldName: "ID",
      debug: false
    });
    // Prep for update get all books
    var item_type = getItemType(list_name);

    var item = {
      "UserID": user_id,
    }

    var updated_book = book;
    updated_book.user_id = user_id;

    getListItemWithId(book.book_id(), function(data){   
        var temp_uri = data.__metadata.uri;
        $.ajax({
          url: temp_uri,
          type: "POST",
          contentType: "application/json",
          data: JSON.stringify(item),
          headers: {
                "Accept": "application/json;odata=verbose",
                "X-RequestDigest": $("#__REQUESTDIGEST").val(),
                "X-HTTP-Method": "MERGE",
                "If-Match": data.__metadata.etag
            },
          success: function (data){
            console.log('Success!');
            self.library.replace(book, updated_book); ***************
          },
          error: function(data){
            alert('Error has occured');
          }
        });
    }, function (data) { alert('Error has occured'); });
  }

// Query all books
  function getBooks(){
      var url = 'http://portal.internal.urs.org/tools_services/training_library/_vti_bin/listdata.svc/Book';
      $.getJSON(url, function(data){
        for (var i = 0; i < data.d.results.length; i++){
            var book = data.d.results[i];
            var user_id= null;

            if (book.UserID != null){
              user_id = book.UserID;
            }
            self.library.push(new Book(book.Title, user_id, book.Id));
        }
      });
  }

HTML

<table class="LibraryTable">
    <thead><tr>
    <th>Title</th><th>Check Out</th>
    </tr></thead>
    <tbody data-bind="foreach: library">
        <tr><td data-bind="text: title"></td>
            <td>
                <span data-bind="ifnot: user_id">
                    <button class="btn_checkout" type="button" 
                    data-bind="click: $parent.checkOutBook">Check Out</button>
                </span>
                <span data-bind="if: user_id">
                    Checked Out
                </span>
            </td>
        </tr>
    </tbody>
</table>
5
  • 2
    aren't book and updatedbook the same? var updated_book = book; Commented Mar 18, 2014 at 17:03
  • sorry that was a typo, but the problem still exists. Commented Mar 18, 2014 at 17:06
  • try to call directly self.library(updated_book); Commented Mar 18, 2014 at 17:53
  • I tried doing that and it ended up replacing the whole array with the one item. Commented Mar 18, 2014 at 18:12
  • Gary S is absolutely right, and that's your whole problem. book and updated_book are both pointers to obj, so your replace function replaces obj with obj, which does nothing. If you make your array items ko.observables then book.user_id(user_id); will update your UI. Otherwise you have to delete book from the array and re-insert it, because observableArrays only notice additions/deletions, not changes in the properties of their items. Commented Jan 6, 2015 at 9:30

4 Answers 4

2

I would recommend using the standard splice method that observableArrays implement for you

self.library.splice(self.library.indexOf(book), 1, updated_book);

The documentation for this, and all the other goodies observable arrays implement is here

Sign up to request clarification or add additional context in comments.

Comments

0

In my code I was setting updated_book.user_id wrong
it should be updated_book.user_id(user_id)

Comments

0

There are two ways to do it:

  1. using splice
  2. using replace

The first one is documented in Knockout docs, but the second isn't: Knockout Obervable Arrays Docs. However, if you look at the source code you'll see that both functions are implemented (at least in KO 3.0, I don't know if replace was missing in previous versions).

The syntax for this operation is this one:

obsArray.splice(
  index,      // at index
  1,          // remove 1 element
  newElement  // and add all the elements passed as the following parameters
);


obsArray.replace(
  originalElement,   // replace the element passed as 1st parameter from the array
  newElement         // with the eleemnt passed as second parameter
);

Comments

0

The problem is that book and updated_book are both pointing to the same object. In your checkOutBook function:

var updated_book = book;          // book and updated_book point to the same obj,
updated_book.user_id = user_id;   // so this also sets book.user_id 

If you use Firebug or Chrome debugger to look at their values after assigning user_id, you will see that book.user_id and updated_book.user_id both have the new value.

ObservableArrays do not track what happens to the properties of their contents. They only track changes to the array itself, i.e. adding items and deleting items. If you want a change to an item's properties to reflect in the UI, you can do it two ways:

Delete that item from the array and re-add the changed version.

Make each array item a ko.observable.

I think the second approach is more straightforward, because then simply setting book.user_id(user_id) would update the UI.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.