Passing Data Between Pages in jQuery Mobile

Coming from Flex one of the things I’ve struggled a bit with is passing data between views in my jQuery Mobile applications. The template approach with Mustache worked really well, but it also had a lot of overhead. In fiddling around today I found a more hacky way that seems to work pretty well (though I think the Mustache route is still better). It relies on the fact that the pagebeforeshow method provides a prevPage property as part of its data object. That prevPage property is just a representation of everything in DOM of the previous page. That means it’s relatively easy to use selectors and that object to pass data to the new page.

The setup of my pages is as follows. My first page has a list of breweries, and each brewery page has a list of the beers that brewery uses. When you click on any of the beers, it goes to a form that can be used to rate the beers and provide some extra information about them. My goal was to prepopulate some of those forms with the beer data I already had. For instance I know the beer name, brewery, and style of each beer, so I should be able to prepopulate those, but they change for every beer so it has to be dynamic.

Within each page I defined a hidden div with two span elements with ids that represent the brewery name and brewery location (since they’re the same for every beer). Then within the beer list I have a hidden div that contains the beer name and the beer style. Here’s an example:

<div data-role="page" id="aleasylum" data-theme="e" data-add-back-btn="true">
     <div data-role="header">
          <h1>Ale Asylum</h1>
     </div>
     <div style="display:none">
          <span id="brewernameinfo">Ale Asylum</span>
          <span id="brewerlocationinfo">Madison, WI</span>
     </div>    
     <div data-role="content">    
          <ul data-role="listview">
               <li data-role="list-divider">Year Round Beers</li>
               <li>
                    <a href="#beerdetails">
                         <img src="images/hopalicious-thumb.gif" />
                         <h3>Hopalicious</h3>
                         <p>5.8% abv. Eleven separate additions of cascade hops give this American pale ale its lush citrus aroma and bold hop flavor without crazy bitterness. Hopalicious is available year round in six packs and on tap throughout the Madison and Milwaukee regions.</p>
                         <div style="display:none">
                              <span id="beernameinfo">Hopalicious</span>
                              <span id="beerstyleinfo">IPA</span>
                         </div>
                    </a>
               </li>
               <li>
                    <a href="#beerdetails">
                         <img src="images/madtown-nutbrown-thumb.gif" />
                         <h3>Madtown Nutbrown</h3>
                         <p>5.5% abv. Our nutbrown ale is velvety smooth with a rich caramel aroma. We blend seven different malts for just the right touch of sweetness and a creamy finish youÔøΩll really dig. Madtown Nutbrown is available year round in six packs and on tap throughout the Madison and Milwaukee regions.</p>
                         <div id="info" style="display:none">
                              <span id="beernameinfo">Madtown Nutbrown</span>
                              <span id="beerstyle">IPA</span>
                         </div>                        
                    </a>
               </li>

So in order to grab that, I just added an event listener to the pagebeforeshow method that uses selectors to grab the data. One of the critical parts is that jQuery selectors have an optional context property that can be set so that jQuery only selects from that context. In this case, since my pages are all using the same id names for the values, I need to set the context so that the selectors are only pulling from the previous page and not the entire document.

 $('#beerdetails').on('pagebeforeshow',function(e,data){
     var beername = $('.ui-btn-hover-e #beernameinfo',data.prevPage).text();
     var brewername = $('#brewernameinfo',data.prevPage).text();
     var brewerlocation = $('#brewerlocationinfo',data.prevPage).text();
     var beerstyle = $('.ui-btn-hover-e #beerstyleinfo',data.prevPage).text();
     $('#beername').val(beername);
     $('#brewername').val(brewername);
     $('#brewerlocation').val(brewerlocation);
     $('#beerstyle').val(beerstyle);
 });

The code above executes before anything gets displayed on the new page, grabs the values from the first page with the hidden div tags, and then sets some of the form fields to those new values.

It strikes me as a bit odd that there isn’t an easier way to do this, so it could be that I’m just not googling the correct thing and jQuery Mobile has something built-in to help with this issue. It does seem like this can be done by using URL variables, but what I like about this approach is that it’s a bit more semantic and there’s no required parsing of the URL string.

  • http://www.coldfusionjedi.com Raymond Camden

    I ran into this myself for the first time last week. I ended up using URL variables and it worked fine. Once you write a nice utility to return a struct of params it’s pretty trivial to use. 

    I have to ask – why is beerstyle and beernameinfo hidden? Are you using that just for data you can suck out for the form? If so – you should really use data attributes instead. Something like…

  • Shaun

    I have simply been using JavaScript “global” objects and HTML5 data-* attributes.  When using pure AJAX page loads in jQuery Mobile, the JS context remains intact as new pages are injected into the DOM.  I know global data is bad, but it is too easy to avoid.  Am I missing something?  I know as Flex vets we are all learning.  

  • http://blog.digitalbackcountry.com Ryan Stewart

    Ahh, good point, the data attributes would be way better. Good call!

  • http://blog.digitalbackcountry.com Ryan Stewart

    I think that probably works just fine, it feels a little bit wrong, but it works just fine, so seems like it’s a good way to go.

  • http://www.dehats.com David Deraedt (adobe)

    Thanks for raising this question Ryan. I’m also in the process of learning how to adapt flex based RIA principles to the web standards world, and behind this topic lies a larger issue which seems critical to me.

    The fact that jQuery Mobile does not expose an easier mechanism for passing data between the views might be surprising, but in all fairness, it’s not its job. It is not an architectural framework. Besides, in my opinion, Flex Mobile ‘s own mechanism (the data object passed between the views) was probably not a great example of how to enforce RIA best practices.

    From an architectural point of view, I’m not sure passing URL parameters is a satisfying answer. In the case of a RIA, we have much greater capabilities at our disposal. This is especially true with jQuery mobile for which there’s actually only one document in which we rarely navigate between HTML pages: we have views dynamically displayed, much like we do in flex. So we’re dealing with a context capable of passing complex objects dynamically to any part of the application. So, apart for deep linking issues, we don’t have anymore reason to rely on URL parameters than we have in flash based apps.

    For such a trivial application, it seems to me just fine to have one globally accessible object. In my case, I have a myTestApp object, instance of a MyTestApp “class”, which exposes public methods for retrieving data and passing it to the views. 
    When a user action requires loading a dynamic view, I don’t rely on href URLs. I listen to the onclick event to call a method on myTestApp which will
    a) optionally do some business logic (synchronous or not, like loading data)
    b) manipulate the DOM of the destination view to populate it with the data, sometimes using underscore.js templates
    c) navigates to the view, by code, using the $.mobile.changepage(destination, options).

    In larger apps, it looks like a better approach would be to use a combination of the Module pattern and a pub/sub mechanism – using a mediator for instance, but this is still quite theoretical to me at this point.

  • JP Cooney

    Why use id’s instead of classes for the spans? Html id’s are supposed to be unique. Furthermore the jquerymobile documentation states that id’s should be unique not only to the a page, but across a site since multiple pages can be in the DOM at any time. http://jquerymobile.com/test/docs/forms/docs-forms.html

    If you have a click event handler when the user clicks on the beer you could get the info with something like this:
    var beernameinfo = $(this).find(“.beernameinfo”).text();
    and use that to populate the form before the form is displayed.

  • Theaellen

    I found a way to pass data from one page to another is to use the clone method.  It will grab all the children of a node as one object and make a copy of it on your new page.

  • Erhan Harmankaya

    Hi could you check out this question 

    I’ve a index.html page. Also this page contains lots of page like #home, #list #contacts etc.in #list part i dynamically get data from my webpage and generate listview. I want that, when user click any of list item, redirect to #imageDetail page and pass image URL to page and show imagehere is the #imageDetail page part   
         
            Image Detail
           
                     
               
               
               
             
        And below code is my javascript code to get json data dynamically.

     
        $(‘#last5′).live(“click”, function() {
            $.ajax({
                url: “http://mysqlservice.com/getdata.json”,
                dataType: ‘jsonp’,
                success: function(json_results){
                    $(“#imageListDetay”).html(”);
                    console.log(json_results);
                    $(‘#imageListDetay’).append(”);
                    listItems = $(‘#imageListDetay’).find(‘ul’);
                    $.each(json_results.results, function(key) {
                        html = ”+json_results.results[key].screen_name+”+json_results.results[key].resim_url+’ ‘+json_results.results[key].adres_data+”;
                        listItems.append(‘‘+html+’‘);
                   });
                    $(‘#imageListDetay ul’).listview();
                },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                    //error
                }
            });
        })

        $(“#detailedIMAGE”).live(“pagebeforeshow”, function (e, data) {
             var brewername = $(‘#detailed_image’,data.prevPage).text();
             $(‘#brewername’).val(brewername);  
             $(‘#imageDetayURL’).attr(‘src’, ‘http://mobil.harmankaya.org/’+brewername);
             alert(brewername);
         });
       

    The problem is after page change. alert(brewername) fires. But list all image urls that listed in listview not my selected.How can i fixed this issue

  • Erhan Harmankaya

    Hi could you check out this question 

    I’ve a index.html page. Also this page contains lots of page like #home, #list #contacts etc.in #list part i dynamically get data from my webpage and generate listview. I want that, when user click any of list item, redirect to #imageDetail page and pass image URL to page and show imagehere is the #imageDetail page part   
         
            Image Detail
           
                     
               
               
               
             
        And below code is my javascript code to get json data dynamically.

     
        $(‘#last5′).live(“click”, function() {
            $.ajax({
                url: “http://mysqlservice.com/getdata.json”,
                dataType: ‘jsonp’,
                success: function(json_results){
                    $(“#imageListDetay”).html(”);
                    console.log(json_results);
                    $(‘#imageListDetay’).append(”);
                    listItems = $(‘#imageListDetay’).find(‘ul’);
                    $.each(json_results.results, function(key) {
                        html = ”+json_results.results[key].screen_name+”+json_results.results[key].resim_url+’ ‘+json_results.results[key].adres_data+”;
                        listItems.append(‘‘+html+’‘);
                   });
                    $(‘#imageListDetay ul’).listview();
                },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                    //error
                }
            });
        })

        $(“#detailedIMAGE”).live(“pagebeforeshow”, function (e, data) {
             var brewername = $(‘#detailed_image’,data.prevPage).text();
             $(‘#brewername’).val(brewername);  
             $(‘#imageDetayURL’).attr(‘src’, ‘http://mobil.harmankaya.org/’+brewername);
             alert(brewername);
         });
       

    The problem is after page change. alert(brewername) fires. But list all image urls that listed in listview not my selected.How can i fixed this issue

  • http://www.facebook.com/profile.php?id=526138809 Doug Boude

    an app i created is a series of pages in a wizard (most of the pages are NOT in the index.html file, but retrieved via ajax). In order for me to be able to show that page’s current selection whenever a user traverses backwards through the wizard, I used JQM’s “JQMData” method to dynamically associated info with a specific page. Additionally, I leveraged HTML5′s sessionStorage pretty heavily. Between the two of them, I was able to come up with a clean solution for “passing” data between pages.

  • http://www.facebook.com/profile.php?id=526138809 Doug Boude

    an app i created is a series of pages in a wizard (most of the pages are NOT in the index.html file, but retrieved via ajax). In order for me to be able to show that page’s current selection whenever a user traverses backwards through the wizard, I used JQM’s “JQMData” method to dynamically associated info with a specific page. Additionally, I leveraged HTML5′s sessionStorage pretty heavily. Between the two of them, I was able to come up with a clean solution for “passing” data between pages.

  • http://www.facebook.com/alain.zelink Alain Zelink

    Why was it unapproved ? Because the code is not in code mode ? how do I post some code (it’s not explained anywhere here).

  • http://blog.digitalbackcountry.com Ryan Stewart

    Looks like it just automatically unapproved it because of the code. Just approved it.