Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I delay page transition in jQuery Mobile until page data is ready?

I have a mobile single-page web application that is built using jquery-mobile (jqm) and knockout. The application itself has multiple pages but they are all contained within a single HTML document.

Problem: after changing my "create view model for page" from sync to async behavior, I have the problem that jquery-mobile fires its events before the data is ready.

Background: up until recently I had been working with sample data, basically a huge JSON blob, and everything worked smoothly. With the new async composition of view models from various sources, data is not ready immediately and my "buildViewModel" method takes a continuation callback instead of just synchronously returning data.

I'm subscribing to the pagebeforecreate and pagebeforechange events, and fire off the code to populate the viewmodel here. The problem is that after returning from the event handler, jqm triggers the remaining chain of events before the data is available. This causes a page transition to an unprepared page, which is undesirable.

I have tried to call event.preventDefault in all of the before-events and manually calling $.mobile.changePage once the page is ready to be a) enhanced and b) the page transition to occur, but without any luck.

I've scanned the jquery-mobile source, but couldn't spot anything that looked like it would allow me to delay the pagebeforeshow event, which is essentially what I need in order to be able to render the page properly.

How can I ensure that 1) data is available and 2) knockout has been applied to perform initial DOM manipulations, before jquery-mobile attempts to enhance the page and before it executes the in-transition to the page?

I also considered using synchronous ajax to fetch resources, but this will (I think) not work for resources loaded from the device (using PhoneGap/Cordova), and has other negative consequences that I'd like to avoid.

FWIW, I'd like to avoid having to manually handle all navigation events by wiring up click-handlers everywhere, but I'm open to all solutions if need be.

Apologies if this is a duplicate; I've searched and read a ton of questions, but not found an answer or question that was quite the same. It just sounds incredible that I would be the first to hit this problem, as I imagine it is a common scenario..

Update: clarified problem scenario description.

like image 752
Morten Mertner Avatar asked Dec 23 '12 23:12

Morten Mertner


3 Answers

I had this exact same problem.

The only solution I've been able to come up with is to write a custom transition handler that defers starting the transition until the Ajax request completes.

Here's a fiddle showing the technique. The fiddle doesn't use Knockout, but does show how to defer the transition.

Basically, since $.ajax() returns a promise, I can pipe that into the promise returned by the default transition handler and return it from my new handler.

In my pagebeforeshow handler, I attach the Ajax promise to the page so that the transition handler has access to it. Not sure if this is the best way, but I liked it better than using a global variable.

The only thing I didn't like about this is that it delays the start of the transition until the Ajax response arrives so it could feel like the page has "hung" to the user making them click again. Manually showing the loading message makes it feel a bit more responsive.

Hope this helps and please let me know if you find a better solution!

like image 133
Jason Diamond Avatar answered Nov 15 '22 21:11

Jason Diamond


Delaying the transition to a new page until its content is ready is a very common issue when facing dynamic content in jQuery Mobile. The most convenient ways to address this are:

  • Instead of classic href type navigation, base the links on "click" actions that will first retrieve the content, build a new page in the DOM, and then initiate a transition to this new page through $.mobile.changePage. The advantage of this approach is that it is easy to put in place, the disadvantage is that you do not navigate with classic href links

  • Bind the pagebeforechange event at the document level to detect if upon navigation the target page is one of your page that should contain dynamic content. In such a case, you can prevent default navigation from happening, take your time to generate the page, and transition upon success. This is described in the JQM docs on dynamically injected content. The advantage is that you can still rely on standard href links navigation, but it requires a bit more code and design upstream to properly detect and act upon navigation to the pages.

    $(document).on( "pagebeforechange", function( e, data ) {
        if ( typeof data.toPage === "string" ) {
             if ( data.toPage === "myDynamicPageName" ) {
                 e.preventDefault(); //used to stop transition to the page (for now)
    
                 /*
                    Here you can make your ajax call
                    In you callback, once you have generated the page you can call
                    $.mobile.changePage
                    (you can pass the Div of the new page instead of its name as
                    the changepage parameter to avoid interrupting again the page change) 
                 */
              }
          }
    });
    
like image 21
Romain Avatar answered Nov 15 '22 20:11

Romain


Set your link to call a "load" function instead of doing a page transition. In your load function, display the "loading message" and make the JSON call. Finally, in the JSON callback function, change page to page2

The load function:

function loadPage2() {
    /* show wait page */
    $.mobile.loading( 'show', {
            text: 'Loading massively huge dataset',
            textVisible: true
    });

     /* perform JSON call then call callback */
 }

Callback function

function callback() {
    $.mobile.changePage("#page2");
}

Here is a working JSFiddle: http://jsfiddle.net/8w7PM/

Note that if you don't want users to be able to update input fields in Page 1 while waiting, introduce a "wait page" between page 1 and page 2, with the init of "wait page" doing the same as "loadPage2".

like image 3
donramos Avatar answered Nov 15 '22 20:11

donramos