Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout bindings gets unbind after history back

I am building a web application using knockout and rails 4. I have a home controller to serve the main htmls and javascripts. Inside application.js.erb I declared my viewModel:

var appViewModel = function appViewModel(){
    var self = this;
    self.navLinks = ['whoarewe','business'];
}

$(document).ready(function() {
    ko.applyBindings(new appViewModel());
});

home/index.html.erb looks like so:

<div class="app_navbar">
    <ul class="navLinks">
        <!--ko foreach:navLinks-->
        <li><a data-bind="text: $data"></a></li>
        <!--/ko-->
    </ul>
</div>

<%= video_tag "Student_Resume1.mp4", :size => "320x240", :controls => true, :autobuffer => true %>

It works fine when I request the page from the server via normal browser request (enter from URL text box) or refresh. My problem is when I switch to another page and use the browser history back in order to go back to home/index (where knockout model is defined), then I get html without knockout bindings.

Is there something I am overlooking?

Update:

After debugging (doing history back) I was able to see that document.ready method is getting called and the applyBindings method as well, loading all the knockout bindings.
It looks like not all static content was loaded, like images. Only after the jQuery completed() method finished, the missing static content was loaded and all the bindings were undone.

Update 2:

After digging some more (binary search the html, removing half of the code each time to see if the bag gets removed) I found that removing some elements from my html solves the problem (bootstrap tabs, and a video tag). It makes sense that the tabs might cause an unreported error from bootstrap or something, but what I can't understand is why the video tag causes it. The video tag is as simple as:

<video>
    <source src="/assets/Student_Resume1.mp4" type='video/mp4' />
</video>

removing it solves the problem; adding it makes some kind of error(I assume). When using history back (only) the video is not shown and the knockout bindings fail.

Update 3:

Removing the video tag doesn't solve the problem as well, it only makes it less likely to happen. This makes me think it might be caused due to page overload or some kind of race condition.

Update 4:

I noticed that calling again ko.applyBindings from chrome console window, rebind the bindings without a problem. So tried to move the applyBindings to $(window).load without success.

Update 5:

I noticed as well that the video tag is not working after history back. Network tab in chrome have 3 requests for the video. 2 got the following response 304 Not Modified, and the 3rd got no replay.

Update 6:

After testing with more browsers it looks like it doesnt happens in firefox. The video is not loaded as well because firefox doesnt support mp4.

like image 381
CountOren Avatar asked Aug 03 '14 16:08

CountOren


1 Answers

After playing around with the js dependancies in application.js, I noticed that removing turbolinks (Which comes as defualt in rails 4 app) solves the problem completely, I removed it by deleting the following line from application.js:

//= require turbolinks

And the bindings works. So investigating some more, I found this Article that states:

"One problem arises when you use document.ready in JavaScript, that event only is fired when the DOM has finished loading, but will not be triggered when Turbolinks performs a page change."

I understood that it is common behaviour of Turbolinks to change the state of the page after the jquery ready method and my applyBindings was not applied in the correct time (the normal jquery ready), after doing history back.

In order that applyBindings is triggered correctly I had to bind the function to 'page:load' as well.

So I added again turbolinks to application.js and changed the normal $(document).ready to look like this:

var ready = function(){
    ko.applyBindings(new appViewModel());
};

$(document).ready(ready);
$(document).on('page:load', ready);

And the bindings gets applied correctly.

Further Reading:

Github Turbolinks

RailsCast - Turbolinks

like image 179
CountOren Avatar answered Nov 09 '22 01:11

CountOren