Custom changePage on jquery mobile causes c.data("page") is undefined on second call - jquery-mobile

I'm trying to build some custom navigation in a dynamic application, all screens are obtained from the server and thus I registered the pagebeforechange event and execute my own function.
Everything works as I expected except when I refresh the data I destroy the dynamic pages and try to call the page I was in again using the page Id, but this second time, although my code creates the HTML for the page, jQuery Mobile throws an "c.data("page") is undefined" error.
I bind the pagebeforechange event:
$(document).bind('pagebeforechange', function(e, data) {
if(typeof data.toPage === 'string') {
appobj.dynamicPage(data.toPage, data.options);
}
});
Then in the dynamicPage method I create the HTML for my page based on Underscore.js templates and let jQuery continue changing the page:
$.get('templates/page.tpl.html', function (data) {
html = _.template(data, { /* several template parameters */});
});
page = $(html);
page.appendTo('body').page();
The idea was to use as much of jQM as possible since I'm creating the destination page and injecting it into the DOM.
When I need to update the supporting data, that I store in localStorage, I just find all dynamic pages and destroy them:
var current = $.mobile.activePage.attr('id');
$('.dynamicpage').remove();
$.mobile.changePage('#' + current
When running the application I can easily navigate between various screens/pages, even for pages that don't exist when the application starts but if the data needs to be updated (because the user added elements in the application of data in the database changed) then the removal code is executed but the old page is not regenerated ending in a white page with all the DOM contents hidden, but the page I wanted to navigate too seems to be in the DOM (at least firebug tells me so).
If I were to restart development I would probably use Backbone.js to handle my model updates and view changes but for now I'll have to use only jQM. Any suggestions? I understand that jQM is not finding my page but I don't see why since my event should be called and the page regenerated, even with the allowSamePageTransition flag set.
Regards,
Sérgio Lopes

Related

Previously visited pages are continually added to the background

As I click through my JQM site the page i visit is fine and I click through to the next and that is fine but displayed underneath is the previous page, and not only, as I keep visiting pages the previous pages keep getting added to this second, 'background' layer. (at the bottom, even twice if i visit the page twice!)
Why is this occuring? What settings have I not set properly? Is it related to cache or pageremove?
Notes
It is version 1.4 (latest stable JQM)
All new pages are accessed via SUBMIT (ie from a form) not links
All pages except the index are dynamic php pages
If you feel any particluar part of the code that is being used now is relevant, let me know, but i doubt it is.
Jquery Mobile removes pages as it keeps updating the DOM, except in two cases.
1 All pages that are referenced directly, eg the index/arrival page (ist page loaded), are NOT removed (the doctype and head are kept you see).
2 All pages that are loaded via AJAX are removed, except FORMS.
So a site that navigates via forms will not have the pages removed.
So pages that are forms should be removed by inserting a new page. In effect reloading the jquery mobile. So doctype, head etc need to be included, as if each page is a landing page.
How to make a form create a new page?
use
data-ajax="false"
in the form properties.
Downside:
Now you are basically leaving the jqm structure you will not be able to use the nice loading page spinning wheel...
Added advantage
This does mean that external js files are reloaded directly too
If there is a solution which also allows the site to 'stay inside' JQM please post and I will accept it as the answer.
try setup your jquery mobile using this setting
$(document).bind("mobileinit", function() {
$.mobile.ajaxEnabled = false;
$.mobile.linkBindingEnabled = false;
$.mobile.hashListeningEnabled = false;
$.mobile.pushStateEnabled = false;
// Remove page from DOM when it's being replaced
$('div[data-role="page"]').live('pagehide', function(event, ui) {
$(event.currentTarget).remove();
});
});
check my sample code here: https://github.com/datomnurdin/service-finder-mobile

jQuery mobile finds elements from first page when on second page

I've got a two page jQuery mobile app, and within the init function, the following code..
Call to the init function
$(document).on('pageinit', function(){
MyPages.init();
});
init: function() {
$('td[id$="drops"]').each(function() {
console.log("Element: " + $(this).attr('id'));
}),
};
I have elements in page one that match the above, such as '#early_drops', '#late_drops', etc. These elements do not exist on page 2, but when page 2 loads, the elements are displayed in the console just like when page 1 is loaded. What am I missing here?
Thanks very much,
-Adam vonNieda
By default jQuery loads pages using AJAX into the existing page in order to allow the animated transitions. If you don't want this you just need to 'turn off' the AJAX loading.
See description here: http://view.jquerymobile.com/1.3.2/dist/demos/widgets/links/
Links that point to other domains or that have rel="external", data-ajax="false" or target attributes will not be loaded with AJAX. Instead, these links will cause a full page refresh with no animated transition. Both attributes (rel="external" and data-ajax="false") have the same effect, but a different semantic meaning: rel="external" should be used when linking to another site or domain, while data-ajax="false" is useful for simply opting a page within your domain from being loaded via AJAX.
You can also disable AJAX across the entire app by default using global configuration: http://api.jquerymobile.com/global-config/

jQuery mobile issues with offline web app

I'm developing an application using jQuery mobile, that will be using HTML5 offline capabilities (cache manifest, etc).
Basic program is for on-field technicians to view/modify their orders on a tablet with no internet connection. I'm using a local browser database to store the orders.
I have an orders.html page which can view any order - but to pass a parameter to it, I can't use GET parameters, because the program is offline and I can't list every single order in the manifest.
So I have to use hash parameters - eg orders.html#o4572. But jQuery mobile doesn't play nice with this scheme - it uses hash parameters for it's own schemes. When I'm on list.html and there's a link to orders.html#o4572 - it turns the link into list.html#o4752 and stays on the same page.
I can turn off jQuery mobile's link handling by setting $.mobile.linkBindingEnabled = false; but this prevents all ajax navigation - you lose the nice transitions, and pop-up dialogs don't 'just work' anymore, you have to do them manually. And there may be other issues.
Is this the only way of getting this to work properly? I'm just starting to use jQuery mobile, so I feel like I'm missing something.
I have done something similar using the jquery-mobile-router plugin with a single page app that has a offline mode, however it should work the same for a multipage app since with a multi-page app the default behavior (ajax-enabled set to true) of JQM is that it pulls in the second page and attaches it to the DOM of the current page.
Using the JQM router you should be able to do something like this
var router;
var orderHandlerRoute = function (eventType, matchObj, ui, page, evt) {
var params = router.getParams(matchObj[1]);
//use your params to pull data from localStorage
};
router = new $.mobile.Router({
'orders.html(?:[?/](.*))?' : {handler: "orderHandler", events: 'bs'}
, {orderHandler: orderHandlerRoute }
});
You should indeed not use hash parameters for anything else than selecting pages when using jquery mobile.
The standard way to proceed is to pass your parameter with file.html?parameter=value and to retrieve the value through javascript.
You can then process this value with a js function that can for instance retrieve the data with an ajax call if you are online, or read it from local storage if you are offline.
This can be done either by binding the changepage event if you want to generate your pages dynamically based on the data associated to the parameter, or by binding the pageinit event if you want to alter the page after it has been displayed (for instance fill in form elements)
Alternatively, if the use of the cache manifest prevents you from usingthe ?parameter=value syntax, you can use the following approach:
- write your target link as file.html#pagename_itemvalue
- bind the pagechange event in order to override the default behaviour, and instead parse the target value, retrieve pagename and itemvalue, and generate/access the content you want to display. You can see an example of that on this page

jQuery UI Dialog with jqGrid loaded by AJAX is not closing

I have a jqGrid loaded by AJAX inside a jQuery UI Dialog. Everything is working fine, except the Dialog which is not closing. When I click in both buttons, it reaches the alerts, but the Dialog is not being closed.
buttons: {
'Confirm': function() {
alert('OK Confirm');
$('#test-grid').dialog('close');
},
'Cancelar': function() {
alert('OK Cancel');
$(this).dialog('close');
}
}
I've tried with $('#test-grid').dialog('close') and $(this).dialog('close'), but no one works. If I remove the jqGrid loaded by AJAX, everything works fine.
The error console on Firefox and Chrome is empty.
I'm loading the jqGrid page with:
$('#test-grid').load('/grid').dialog('open');
Can anyone help me?
UPDATE
I've tried to load a simple HTML snippet using AJAX and the problem persists.
The problem is that the call to load is interfering with the call to open the dialog. You can fix this by loading the AJAX content into a child element of test-grid. For example:
$('#test-grid-child').load('/grid');
$('#test-grid').dialog('open');
Update
I just read the docs for load and gave this a bit more thought. What is happening is that when the code $('#test-grid').load('/grid').dialog('open'); is executed, an AJAX request is started and the dialog is created immediately. But once the load's AJAX request finishes, jQuery comes back and overwrites the contents of #test-grid. This explains why the dialog could not be closed, because the underlying markup is modified out from underneath the dialog object.
Retrieving data to a child element eliminates this problem since load and dialog each now manipulates a different section of the DOM.
Note that if the AJAX request takes a long time to complete, you might want to consider implementing a complete function to give feedback to the user - maybe by displaying a spinner until the data is ready. For example:
$('#test-grid-child').load('ajax/test.html', function() {
alert('Load was performed.'); // Perform any necessary UI action here
});
Anyway, more information than you probably needed, but I just wanted to update this question while it was still fresh in my mind...

Pre-render JQM pages in the background

I have a mobile web app built with jQuery Mobile. It is using a multi-page template, with most of the content being created dynamically with javascript. The first time the app is loaded it shows a splash screen and then sends the user to an "index" page. From there the user can navigate to different parts of the app via # links. Each page's content is created dynamically with javascript and then inserted into the DOM on the "pageshow" event.
This works very well with one little problem that I haven't been able to solve. The first time a page is loaded it takes several seconds for the content to be generated and inserted into the DOM. Subsequent loads are fine since the previously rendered content is already in the DOM and is only replaced if there is new data. What I want to do is pre-render each page's DOM so that there is no noticeable delay when a page is loaded for the first time. I can achieve this by calling $.mobile.changePage() on each page. The problem is that I want this to be done in the background without the pages or any page transitions being shown to the user. Is there any way to do this?
P.S.: Worst-case scenario, I'd just keep the splash screen on top of everything while the pages are pre-rendered, but I can't even do this since JQM does the transition and changes the page whenever I call $.mobile.changePage().
All you have to do is use the $.mobile.loadPage. In the example below I show how to initialize a single page by hash URL upon the showing of #one. You could list out all of them if you wanted or create a fancy script to find all the [data-role] elements that do not have a class of ui-page, grab it's ID, and call a load on in the same way I have here.
$("#one").live("pageshow",function(){
$.mobile.loadPage("#two");
});
You can see the example in action on this jsfiddle..
I stumbled upon your question since I was looking into somewhat the same issue. What I ended up with is a _preloadPages() function in my project basically instantiating the page widgets which normally are done internally. Since this is not documented please keep in mind this can break anytime when upgrading JQM.
To control which pages to preload and which not I've introduced a class 'preload' which I place on the page containers I want to preload. This _preloadPages() function I call during splash screen and to not stress the initialization of my app to much the preloading is done in a 300ms interval. Still playing with these numbers though.
function _preloadPages()
{
// Execute the page() widget call on all pages that haven't yet been initialized, we do this
// to prevent a slight delay when initially loading a page
//
var $pages = $( "[data-role='page'].preload:not(.ui-page)" )
, index = 0
, pageCount = $pages.length
;
var preloadTimer = setInterval( function()
{
var page = $pages[ index ]
, $page = $( page )
;
$page.page();
index++;
if ( index === pageCount )
{
clearTimeout( preloadTimer );
}
}, 300 );
}

Resources