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>
JotaBe
  • 38,030
  • 8
  • 98
  • 117
AustinT
  • 1,998
  • 8
  • 40
  • 63
  • 2
    aren't book and updatedbook the same? `var updated_book = book;` – Gary.S Mar 18 '14 at 17:03
  • sorry that was a typo, but the problem still exists. – AustinT Mar 18 '14 at 17:06
  • try to call directly self.library(updated_book); – Akhlesh Mar 18 '14 at 17:53
  • I tried doing that and it ended up replacing the whole array with the one item. – AustinT Mar 18 '14 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. – Doug Leary Jan 06 '15 at 09:30

4 Answers4

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

Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
0

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

AustinT
  • 1,998
  • 8
  • 40
  • 63
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
);
JotaBe
  • 38,030
  • 8
  • 98
  • 117
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.

Doug Leary
  • 199
  • 8