Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

how to trigger code on first displayed page? #12

Closed
drewda opened this Issue Dec 8, 2011 · 15 comments

Comments

Projects
None yet
5 participants

drewda commented Dec 8, 2011

I see in the readme that there is an issue with the "empty hash" on the first load of a multipage application. Is there any way to trigger some code when showing the first page (the one with the empty hash)?

I've tried creating a defaultHandler, but that doesn't seem to get trigger on the first load.

Owner

azicchetti commented Dec 9, 2011

Il 09/12/2011 00:52, Drew Dara-Abrams ha scritto:

I see in the readme that there is an issue with the "empty hash" on the first load of a multipage application. Is there any way to trigger some code when showing the first page (the one with the empty hash)?

Hi.
Yes, you just have to write a route as if the hash is the id of the
first page.

There's an example under examples/test.html.

To sum up, if your first page has id="index", the route would be "#index".

drewda commented Dec 9, 2011

Thanks for your quick response, @azicchetti

Hmm, that's what I'm trying to do, but my handler only gets triggered upon returning to that initial page, not on its initial load. Do you have any more tips?

By the way, this is my code (in CoffeeScript):

window.masterRouter = new $.mobile.Router [
  { "#open-project": { events: "s,i,c", handler: "openProject" } }
  { '#project(?:[?](.*))?': { events: "s", handler: "project" } }
  { "#count-session(?:[?](.*))?": { events: "s", handler: "countSession" } }
  ], 
    openProject: =>
      console.log "projectOpen"
    project: (eventType, matchObj, ui, page, evt) =>
      hashParams = getHashParams()
      if !hashParams.projectId
        console.log "no project id!"
      else if hashParams.projectId
        console.log "project: #{projects.get(hashParams.projectId).get('name')}"
      # segment selected?
    countSession: (eventType, matchObj, ui, page, evt) =>
      console.log "countSession"
  , 
    ajaxApp: true

My first page begins like so:

<div data-role="page" id="open-project">
Owner

azicchetti commented Dec 9, 2011

Il 09/12/2011 01:43, Drew Dara-Abrams ha scritto:

Thanks for your quick response, @azicchetti

Hmm, that's what I'm trying to do, but my handler only gets triggered upon returning to that initial page, not on its initial load. Do you have any more tips?

everything seems ok, it should work. I'm using almost the same code in
all my projects.

Is your snippet executed before the mobileinit event?
The route on the first page must be set before pageload and mobileinit
events in order to work properly.
You may also refer to the examples/test.html file, where there's an example covering your scenario (the #index route).

If the problem persist, please provide the full source code example so
that I can help you.

@azicchetti azicchetti closed this Jan 10, 2012

I find this a little confusing. If I understand correctly, the router won't 'fire' on the first page that's loaded, unless it was defined before the jquery mobile script is included in the page.

In all of the examples here on github, the router isn't defined until after the jquery mobile script is included in the page.

In the examples, you're loading scripts in the same order I am:
jquery, jquerymobile-router, jquerymobile, [underscore, backbone,] app

Assuming I define my router in app, does this mean it will never fire on the first page a visitor sees?

If that's the case, how do you handle the initial page load when your router needs to be aware of backbone?

Owner

azicchetti commented Jan 23, 2012

Hi,
in order to work properly, the router script must be included before jquery mobile, but usually this has nothing to do with the routes you intend to set up.

However, since the router relies on jquery mobile events, if you need a route for the first page, you have to set it up before the associated event is dispatched.

To sum it up: don't create the router in the document ready event, because it may be too "late" to catch the mobile events that are fired for the first page.

Under examples/test.html you can see a working example for the "#index" page, which is the first one to be displayed by jQM. The same rules apply for non-multipage applications.

Hope this solves your problem.

Cheers

Clear as day - thanks for the detailed explanation.

drewda commented Feb 23, 2012

@azicchetti, thanks again for the explanations. I finally got around to fixing my code according to your instructions.

I'm still struggling with this despite looking at the examples/test.html

I'm using RequireJS to load my modules.

In index.html, I load the main script using:

<script data-main="app/main" type="text/javascript" src="../app/lib/require-jquery.js"></script>

Then in main.js, I have:

require(
['jquery', 'underscore', 'order!backbone', 'order!jqmr', 'order!jqm', 'app' ],
function ($, _, Backbone, jqmr, $$, app) {
require(
['order!jqmr', 'order!jqm'],
function (jqmr, $$) {

        $.mobile.hashListeningEnabled = false;
        $.mobile.pushStateEnabled = false;
        $.mobile.page.prototype.options.degradeInputs.date = true;
        $.mobile.defaultPageTransition = 'none';

       app.init();
    });

} );

Within app.init(), I create a jquery-mobile router:

var ws = new $.mobile.Router(
[
...
...

However, if I'm understanding the comments correctly, creating the router in app.init() is too late to catch the initial pageChange event from jquerymobile? It also sounds like the router needs to be created before jquerymobile is loaded?

If this is the case, and assuming my router needs to access views/models declared within app.init(), how can one properly design the router? It seems like a chicken / egg thing. Note that the router works fine once the app is loaded, it just doesn't catch the first page event. I feel like I'm missing something fundamental about how to properly architect the router

Owner

azicchetti commented Mar 12, 2012

I'm sorry but I have no experience with requirejs, so my help is really limited in this field.

As per your questions, the router can and should be instantiated after jquery mobile is loaded, but before the first pagechange event, if you need to react to that event.

App init is dispatched after all the modules have been loaded, I suppose (please correct me if I'm wrong).
I don't know whether this is before or after the first pagechange is dispatched by jquery mobile, but if you're unsure or have troubles, you can synchronize everything with a $.Deferred.
You just have to instantiate the router as soon as the jqm-router module is loaded, then use the deferred done() method in the first-page handler. The deferred will be resolved during app init.
This is the worst case scenario, since I'm assuming that the pagechange happens before app.init. More tests are required to confirm this thing.

As a side note: $.mobile.hashListeningEnabled = false has the potential to make you cry for the multiple back button issues it arises, please make sure you really want that setting

I'm really looking forward to hearing from you about RequireJs integration, so that I can write a proper paragraph on this subject in the documentation.

Thanks for your efforts on this matter

Hi Andrea,
first thank you for your work. I run in similar problems. If i understand you right the current ordering is something like this.

Loading:
1.) jQuery
2.) jquery.mobile.router
3.) jquery.mobile
4.) app.js

On 4.) in the app we instantiate the mobile router but this seems to be to late. If the file dependencies are loaded to slow the pagechange event will be triggered before we are done with the instantiating. do you have some ideas how this can be solved ? is instantiating possible before mobileinit event triggers ?

thanks Dennis

Owner

azicchetti commented Sep 26, 2012

Hi,
since my experience with require js is very limited, I guess I'll need a zip file with an example to properly help you here.

I guess you can instantiate several routers for each module, using string function names as handlers and providing each router an object reference in which it will look the function name up.

The object can be "mostly empty" (that is to say, just the skeleton) during instantiation.

For example (this must be executed ASAP during app bootup):

myapp.modulename={
        handlers: {},
        router: null
}; 

myapp.modulename.router=new $.mobile.Router([
        { your routes here }, { ... }, ...
],myapp.modulename.handlers);

When require.js loads the modulename.js file, you'll extend myapp.modulename and "fill" the empty objects with handlers and other things (use the $.extend function, do not replace object references with new ones).

As a side note: if you're using jQM 1.2.0-rc* versions, there's a bug in jQM itself that prevents deep linking to a certain internal page in a multipage template (and so the first displayed page will never show up properly), please check whether your problem falls into this case.

Hi Andrea,

thanks for your fast reply. i solved it currently by deactivate the autoInitializePage with:

$.mobile.autoInitializePage = false;

option from jQuery Mobile and trigger

$.mobile.initializePage();

after app init.

I dont think its a requireJS Problem if your app.init is "large" and need some time on loading (async) you run into the same problem. (jQM triggers first event before app.init is run)

Owner

azicchetti commented Sep 26, 2012

Well, this is going straight to the docs, it seems a clever solution and probably the best approach w/ require.js.

Thanks for figuring this out.

I can create a test patch with require js if you want

Owner

azicchetti commented Sep 26, 2012

It would be great

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment