Refactor: Navigation Plan

jblas edited this page May 26, 2011 · 16 revisions

Refactor: Navigation Plan

Over the last few alphas, we've gotten feedback from the community regarding jQuery Mobile's current navigation implementation. Most of the feedback centers around a few key things:

  • Allow for loading page content transmitted in a format other than HTML. (JSON, XML, etc)
  • Allow developers to encode/decode what is stored in the hash.
  • Allow developers to turn off hash management entirely so it can be managed manually by developer code.

For jQuery Mobile Beta 1, we are going to take steps towards addressing some of these issues by refactoring the page loading and navigation code so that it can be extended or overridden. Below, we discuss some of the planned changes in detail. Not all of them are going to make it into the Beta 1 release, but we are interested in hearing any ideas or concerns you have around some of these plans.

Refactor Navigation Code (Beta 1)

We need to refactor the navigation code to allow for extensibility and hooks. As of version 1.0a4.1, the navigation code was largely implemented as a set of nested functions with the $.mobile.changePage() function in jquery.mobile.navigation.js. The problem with this current implementation is that these nested functions use and manipulate variables from their outer scope. This makes the code very hard to follow.

We need to refactor this code into the 3 operations that are being performed:

  • loadPage()
    • Responsible for loading a page into the DOM of a specific page container and enhancing it.
  • changePage()
    • Responsible for updating the internal bookkeeping for tracking what is the current page. This includes:
      • Managing the URL stack.
      • Managing the location hash.
      • Kicking off a transition.
  • transitionPages()
    • Responsible for managing the transition between the current active page and the new page to be shown.

Partitioning the code in this way will make it easier for us to identify key points for adding extensibility hooks. It will also make the code easier to follow.

Notes

  • The signature for changePage() has been changed during this refactoring. changePage now takes 2 arguments, the first is required, but the latter argument is optional. changePage() takes a URL or a jQuery collection containing a page element as its first argument. The 2nd argument is an options object that allows the caller to modify changePage behavior. The options that can be specified are as follows:
    • transition
      • String
      • The transition to use when showing the page.
      • Defaults to transition specified by $.mobile.defaultPageTransition.
    • reverse
      • Boolean
      • Decides what direction the transition will run when showing the page.
      • Defaults to false.
    • changeHash
      • Boolean
      • Decides if the hash in the location bar should be updated.
      • Defaults to true.
    • role
      • String
      • The data-role value to be used when displaying the page.
      • Defaults to "page".
    • pageContainer
      • jQuery collection
      • Specifies the element that should contain the page after it is loaded.
      • Defaults to $.mobile.pageContainer.
    • type
      • String
      • Specifies the method ("get" or "post") to use when making a page request.
      • Default is "get".
      • NOTE: This should probably be renamed to "method". The current name comes from the old signature.
    • data
      • Object or String
      • The data to send with an Ajax page request.
      • Default is undefined.
    • reloadPage
      • Boolean
      • Forces a reload of a page, even if it is already in the DOM of the page container.
      • Default is false.
    • showLoadMsg
      • Boolean
      • Display the loading message when a page request is fired.
      • Default is true.

Potential Issues

  • The changePage() signature change will break apps that call changePage() manually. We've discussed providing a temporary plugin that monkey patches the new changePage() so it can translate a call with the old signatures and map them into the new one.

Status

DONE

Fix Path Management (Beta 1)

The original navigation code for jQuery Mobile used to create deep link urls that contained a relative url in the hash.

http://foo.com/jblas/#mailboxes/inbox

The big problem with this was that it was very hard to tell the difference between an id and a document reference:

http://foo.com/jblas/#mailboxes

when no file extension is specified. This is especially common in apps that rely on re-write rules.

After 1.0a4.1 was released we attempted to switch to using site relative urls within hashes. During this process a few things broke including our base tag management, which resulted in munged image and link urls.

We need to fix this issue as well as address some other issues:

  • Use link.href instead of link.getAttribute("href").
    • No more guessing about how to make relative urls absolute. The href property of a link is the absolute URL as resolved by the browser.
  • Urls from the same domain as the document should be converted to site relative before writing into the hash.
  • Urls that are cross-domain (PhoneGap ONLY) should have the absolute URL written to the hash.
  • Need to store the absolute url used to load a page within data-url.
  • We should build and maintain a url to page element dictionary to avoid having to search the entire document for a given page element with a particular data-url value.
  • If changePage() is called with a relative URL, the data-url on the page should be used to resolve it so that it is absolute. We should never assume that it is should be resolved to the document URL. (Breaks PhoneGap)

Potential Issues

  • May need to update unit tests to account for changes.

Status

Not started yet.

Add Hooks for Page Loading

Several developers have expressed the need for loading content via JSON instead of HTML. While loading content in this manner is not a primary focus of jQuery Mobile, we still want to enable developers to load content in any format they wish without having to hacking/creating their own custom version of jQuery Mobile.

There are 3 possible approaches we can take to allow this:

  • Overriding loadPage()
  • Delegate Model
  • Content Type Model

The approaches themselves aren't mutually exclusive either, for example, we can implement the Content Type Model approach within a loadPage() function.

Overriding loadPage()

This option requires completion of the navigation code mentioned above and requires the least amount of effort by us. With this approach,the developer would simply clone our loadPage() method and tweak it to their hearts delight. The only requirement is that they return a deferred promise object as the default implementation does, and make sure to resolve it when their are done generating and enhancing the content.

Delegate Model

This option involves the loadPage() code being partitioned into "interesting" sections that are wrapped with pre and post notifications. The idea here is that developers can add listeners, for these notifications from outside jQuery Mobile to do additional processing and in the case of "pre" notifications even short-circuit the default code/behavior for that "interesting" section.

Content Type Model

This option involves providing a mechanism that allows developers to register content handlers by type. In this scenario, the current implementation that loads an external HTML document and inserts the first element with data-role="page" into the current document, would be moved into a handler function that is registered with the "html" type.

We could then expose a configuration option:

$.mobile.defaultContentType = "html";

that could be overridden by 3rd party developers that wish to provide their own content loaders. In addition to specifying a defaultContentType, changePage() and loadPage() would be modified so that a contentType could be passed to them as an option to specify which handler to use.

The only requirement we would impose on these handlers is that they return a deferred promise so that we are able to support both synchronous and asynchronous loading handlers. The enhancement of the actual markup that was loaded and inserted into the document would be handled automatically, outside of the handler.

Note that even with this approach, we may want to fire off notifications at "interesting" points within the loading flow.

Potential Issues

Status

Not started yet.

Encode/Decode URL Hooks

Potential Issues

Status

Not started yet.

Make Hash Management Optional

Decouple Hash Management

If hash management is optional, changePage() needs to be modified so that it can fire off the necessary notifications whenever the current page changes. The hash management code, when turned on, should listen for these notifications and update the hash accordingly.

Note that this same mechanism can be used to allow 3rd party developers to hook in and manage the URLs manually.

Potential Issues

Status

Not started yet.