0

I know there are a lot of questions that cover jquery mobile / knockoutjs integration, however I couldn't find a thread that solved my issue. I have a master view model which contains child view models, and so I initialize this on page load, as that event is only fired on application load:

var viewModel = null;

$(function () {
    console.debug("running init");
    viewModel = new ViewModel();
    ko.applyBindings(viewModel);
});

This works great on the first page of my app, however when I go to a child page, the knockoutjs content doesn't show up because jquery mobile has loaded the html dynamically and knockout doesn't know to update the binded content. I'm trying to tell it to update dynamically by using the $(document).delegate function, however I'm struggling with how it's supposed to be implemented.

<ul id="speeding" data-role="listview" data-bind="foreach: speeding.items">
    <li>
        <h3 class="ui-li-heading" data-bind="text: Address"></h3>
        <p class="ui-li-desc" data-bind="text: Address2"></p>
        <p class="ui-li-desc" data-bind="text: PrettyDate"></p>
        <p class="ui-li-aside" data-bind="text: SpeedMph"></p>
    </li>

    <script type="text/javascript">
        var loaded = false;
        $(document).delegate("#page-speeding", "pagebeforecreate", function () {
            if (!loaded) {
                loaded = true;
                ko.applyBindings(viewModel);
            }
            else {
                $("#speeding").trigger("refresh");
            }
        });
    </script>
</ul>

I'm putting the delegate function within the page it's being called on, as apparently that's a requirement of using delegate. Then on first load of this child page I call ko.applyBindings (I only wanted to call this on application load but I couldn't get trigger("create") to work. On subsequent calls it would call trigger("refresh") (which doesn't work for me.) The issue though is that the delegate function gets added each time I go to the child page. So on first load of the child page, it will call the delegate callback function once. If I go back to the main page, then back to the child page, it will call the delegate callback twice, and so on.

Can someone please provide guidance of the recommended approach to refreshing the knockoutjs bindings on child pages?

Serge P
  • 1,173
  • 3
  • 25
  • 39
Justin
  • 17,670
  • 38
  • 132
  • 201
  • I had a lot of problems using knockout with jQM and ended up not using it at all. But I solved this specific point by assigning a id to each page and passing the dom element as a parameter to knockout, e.g. `ko.applyBindings(viewModel, $("#mypage div:jqmData(role='content')[0])`. http://stackoverflow.com/questions/7342814/knockoutjs-ko-applybindings-to-partial-view – jgillich May 06 '13 at 19:27
  • First, use `$('#speeding').listview('refresh')` and replace `.delegate` with `.on` – Omar May 06 '13 at 20:24
  • @Omar - If I do $(document).on("#page-speeding", "pagebeforecreate", function () { }); then the function isn't called at all... – Justin May 06 '13 at 20:50
  • @jgillich - Thanks for the response, but you didn't make it clear when you are applying the bindings. Are you applying the bindings per element id on first load, like I'm doing above (minus the per element id), or are you doing it using $(document).delegate in some way that doesn't have the issue I mentioned above? – Justin May 06 '13 at 20:52
  • Try this `$(document).on('pagebeforeshow', '#page-speeding', function () {` – Omar May 06 '13 at 21:00
  • It still increments how many times it's called each time I go to the page. – Justin May 06 '13 at 22:04
  • You want it to fire once? Use `pageinit` instead of `pagebeforecreate`. – Omar May 06 '13 at 22:13
  • pageinit still increments too. – Justin May 06 '13 at 22:23

1 Answers1

0

This is what ended up working for me. I have no idea if there's a better way or not...

var viewModel = null;

$(function () {
    console.debug("running init");
    viewModel = new ViewModel();
    ko.applyBindings(viewModel);

    var pages = [
        "scorecard", "speeding", "leaderboard"
    ];
    _.each(pages, function (page) {
        $(document).on("pagebeforecreate", "#page-" + page, function () {
            console.debug("applying " + page + " bindings");
            ko.applyBindings(viewModel, $("#page-" + page)[0]);
        });
    });
});
Justin
  • 17,670
  • 38
  • 132
  • 201
  • 1
    This is exactly what I use. I did a lot of searching on this topic and read countless forum posts - there is no better way. They made a JavaScript framework that works badly in JavaScript-heavy applications... – jgillich May 07 '13 at 17:04