Disappearing knockout.js bindings on history.back () when using pjax

I have a test case: http://tremby.net/knockouttest/page1.php

I use pjax for page transitions. Clicking between pages 1 and 2 in the test example above loads two pages asynchronously, and then replaces the contents of the #main element. The state of the browser history is inserted into the process, so the URL in the location bar is updated, and the return button should return us to where we were.

The list on page 1 is filled with the foreach binding by knockout and the List model that I defined (in the built-in script in my head). In ready on page 1, the inline script runs ko.applyBindings , and so the list is populated.

The "add item" button adds an item to the view model, which it then adds to the list.

Adding a few elements, then going to page 2 and then back to page 1, the list is fresh with its initial 3 elements, and the add elements button still works. This is good for our use case.

Adding a few elements and then going to page 2, but on the other hand, using the back to back button to go back to page 1, is my problem. New items are still visible (which is good, and vital for our use case when using the back button of the browser), but the "add item" button is now broken. The code for entering a new element in an observableArray definitely works, but the bindings seem to have disappeared, so the knockout does not know to add a new DOM element.

I cannot run the ko.applyBindings function on popstate , otherwise the knockout will think that I want each of the existing elements to be duplicated for each element in the list, and after that each click on the “add element” gives several new elements.

I have a strong feeling that something obvious is not enough for me, but I can’t find anything like this to help in the documentation. Any help would be greatly appreciated.

+4
source share
1 answer

You see this behavior because, despite binding the view model to the user interface, you have not saved it anywhere so that the browser history can access it in order to rebuild the page in the expected state. This behavior can also be observed using the back and forward buttons built into the browser. The usual model that I choose to solve this problem is this:

First, assign this to a variable, this will save you from hassle later on . Now the view model should look something like this:

 function List(items) { var self = this; self.items = ko.observableArray(items) self.addItem = function(text) { self.items.push(text) } } 

Now create a computed observable that serializes the current state of your view model. It is entirely up to you how you implement it.

 self.toJson = ko.computed(function(){ /*** Serialise self.items() and return the string ***/ return myJsonString; }); 

Then bind the calculated to hidden input, this ensures that the view model is stored in the form data, which allows you to reorient the view model again.

 <input id="serialisedItems" type="hidden" data-bind="value: toJson" value="[1,4,3]" /> 

Finally, redirect the view model to $.ready() this way;

 $(function() { var items = $('#serialisedItems').val(); /*** Deserialise items before passing into the List ***/ var list = new List(deserialisedItems); ko.applyBindings(list); }); 

At this point, you should also indicate that the list was initialized by setting the value <input> attribute.

+1
source

Source: https://habr.com/ru/post/1489994/


All Articles