4

I'm building an HTML5 mobile application using Backbone, require and jQuery mobile as a technology stack. The application is really smoothy with a connection to a backend web service and so on. To Change between the pages I use jQuery changePage. To instanciate Backbone views I use the following strategy:

$( document ).delegate("#card-delivery-address", "pageshow", function(){
    require(["js/views/cardDeliveryAddressViews.js" ], function(cardDeliveryAddressViews) {
        new cardDeliveryAddressViews();
    });
});
  1. $.mobile.changePage('deliveryAddress.html') => changes the current page using jquery mobile

  2. When an event called "pageshow" is fired on #card-delivery-address (which means that my page was inserted in the DOM and successfully rendered) => create the backbone view and bind the $el to an existing DOM and taking control on the DOM events using backbone views.

  3. To pass data and instances between views we use a window.tempData global variable in which we put the context data so that the new view will know what to do.

Doing one way navigation is successful, suppose that I come from view1 --> view2 (with tempData) then from view2 --> view 3 (override the same tempData). Now, and here is my problem: if we want to go back from view 3 --> view 2 we will need the tmpData content that we used to initialize and render the view 2. The same thing when we want to go back to view1.

Note: I'm not using backbone router but I can change to use it if that will solve my problem.

Any ideas guys?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Houcem Berrayana
  • 3,052
  • 22
  • 40
  • I once overrode Backbone.history.navigate so it used HTML5 replaceState to push a state object when navigating. I would assemble this object by asking views to supply any information they want to preserve, hierarchically. On route event, I would get this state object from history API and “give” it back to views. Spent a couple of days on this, but it worked remarkably well. – Dan Abramov Jan 30 '14 at 17:31
  • 1
    I did something like that but no Backbone related. It works on the top of jquery and uses HTML 5 history events. It works very well also – Houcem Berrayana Jan 31 '14 at 05:36

5 Answers5

2

During the page transition previous page is going to be removed from the DOM

To keep all previously-visited pages in the DOM you can add the attribute:

<div data-role="page" id="card-delivery-address" data-dom-cache="true">              
    <!-- [...] -->      
</div>

or by initializing global parameter:

$.mobile.page.prototype.options.domCache = true;
Slim Tekaya
  • 337
  • 3
  • 16
  • That can solve a part of the problem when I can show an instance of a page one time inside a history stack. But if in a same history stack we get 2 instances of a same page we're stuck – Houcem Berrayana Oct 09 '13 at 07:15
2

Ok let's give it another try. We'll implement a data store keyed by pages' path.

var pagesData = {};

// Let's assume we just switched to page '/my/page/1'

// Get a reference to the stored page data if it exists, 
// or create it empty and return it.
var currentPageData = pagesData[window.location.pathname] ||  
                     (pagesData[window.location.pathname] = {});

currentPageData.foo = 'bar';

console.log(pagesData['/my/page/1'].foo); // > "bar"

Now all your pages have a "local" datastore that allows them to save their state/data throughout navigation.

NB: If you don't use pushState, you have to use window.location.hash in place of window.location.pathname as a key in PagesData.

thibauts
  • 1,638
  • 9
  • 8
  • 1
    A view can appear 2 times in the same stack but with different contexts. – Houcem Berrayana Oct 10 '13 at 11:08
  • So the question is instead of using window.location.pathname what can I use to distinguish a view instance ? – Houcem Berrayana Oct 10 '13 at 11:08
  • You probably have a problem at the architecture level. The best way to make your view data persist may be to not destroy and recreate them to begin with. – thibauts Oct 10 '13 at 12:34
  • 1
    You can still use pathname as a key, adding something unique to the view inside the page as a postfix. `pagesData[window.location.pathname + '#viewNameUniqueInThisPage']`. Of course the data you are caching should be related to *view-state only* and not in any way to model data that can be cached and reused in many other ways (google backbone model cache). – thibauts Oct 10 '13 at 12:41
  • AND, if you really need to persist views, cache *them*, not data they contain. First check if a view exists for the key you need, and if it doesn't create it and store a reference in your datastore before using it, kind of like I described in this answer. – thibauts Oct 10 '13 at 12:50
  • @thibauts and me seems to be ending up answering a lot of questions together. thanks thibauts. i learn a lot from your points. – Mohit Oct 10 '13 at 19:54
  • 1
    @HoucemBerrayana it really seems that your code smells a bit. do try to keep view-states and model data different. – Mohit Oct 10 '13 at 19:57
  • @MohitJain I'm all backbone these days :) And thanks, I do too. – thibauts Oct 10 '13 at 20:19
  • You should have updated your previous answer instead of posting another one. – Omar Oct 10 '13 at 20:52
  • @Omar don't you think both answers give different point of view. there can multiple ways of handling a problem and they can come from one person. – Mohit Oct 11 '13 at 14:20
  • @MohitJain you can add 10 solutions in one answer. That's why editing is available and isn't restricted. – Omar Oct 11 '13 at 14:40
  • @Omar posting multiple answers to the same question isn't restricted either. Is there a best practices page that addresses this ? – thibauts Oct 11 '13 at 16:46
  • there is no [best practice](http://meta.stackexchange.com/questions/28471/two-answers-one-question), however, it looks better and comprehensive. – Omar Oct 11 '13 at 17:02
  • I usually use a single answer when I think of 2 solutions, but for this one it felt better with two answers. I guess it's a matter of taste more than anything else in some cases, and this debate doesn't bring much value here. Thanks for the advice. – thibauts Oct 11 '13 at 18:37
  • Thanks thibatus. So do you consider using data-timestamp="124125125" to distinguish between view a good practice ? – Houcem Berrayana Oct 12 '13 at 15:32
  • @HoucemBerrayana if I understand correctly what you're trying to do, using the selector that allows you to bind uniquely the view to the DOM would be a better choice. – thibauts Oct 12 '13 at 19:09
  • exactly . Do you have ideas ? – Houcem Berrayana Oct 13 '13 at 04:46
1

Why not make windows.tempData an object?

windows.tempData = {};

To store a page data:

window.tempData["id_of_page"] = your_data;

On pagebeforeshow:

myDataForThisPage = window.tempData[this.id];  // assuming "this" being your page element
frequent
  • 27,643
  • 59
  • 181
  • 333
  • I can't because one ID can be put many times in the DOM. Also I want to make some cleanup when page is no more in the memory – Houcem Berrayana Oct 09 '13 at 08:40
  • Multiple IDs is not valid HTML and bound to cause problems down the line. Also, I thought your question was about accessing tempData from pages that are no longer in the DOM when the user revisits a page? If so, then you need to store and keep this tempData. If you remove it, of course you will not have it when the user goes back. – frequent Oct 09 '13 at 08:46
  • yes but that it's not enough. I was thinking about putting the context it self in something like history stack and do all the cleaning when history changes. It must no be something like a table .. it should releases context when a page disappear from history (I click back the context on the page stills on history stack, then click if I click another link the page is removed from the stack). It's just like any browser next and previous behavior – Houcem Berrayana Oct 09 '13 at 12:13
  • 1
    well you could try to write an extension to JQM urlhistory (not sure if this can be extended like everything else though). I used to "hack" JQM navigation for a multipanel layout and to do so, I also stored the viewport/pagecontainer in the urlHistory along with the data JQM is keeping. So (a) hack in your tempdata yourself or (b) see if you can create an extension on the url history. Either way would be my preferred solution, because you are not adding any extra layer of logic. I could dig out a sample how to modify url history if you want – frequent Oct 11 '13 at 16:51
  • Modifying history stack ? that's not what I want to do. I have looked into HTML 5 history API there is pushState method and thought about something thatpushes meta data in the original history stack without so much pain. – Houcem Berrayana Oct 12 '13 at 15:29
  • 1
    no, no... that's not what I mean. Check the [JQM JSBin](http://jsbin.com/ofuhaw/167). Open firebug and console **$.mobile.navigate.history**. This is where JQM keeps it's own "history" and stores certain information. Parameter **stack** contains all pages the user visited and which JQM currently keeps in it's history. **Stack[0]** is the current page for which **hash**, **lastScroll** and **url** are stored. This is where you should add the **tempData** as 4th parameter for each page the user visits. If JQM removes from stack your tempData is also cleaned up automatically – frequent Oct 12 '13 at 15:37
1

Using a stack may solve your problem. The internal history handling of your browser uses that to handle navigation and the back button.

Stacks are implemented in Javascript using an Array and its push() and pop() methods. They allow you to save and restore a previous state.

var navigationStack = {...},
    currentState = {...};

...
// When going to a new page, you push (or save) the current state on the stack
navigationStack.push(currentState);
...

...
// When going back to the previous page you pop the previous state from the stack
currentState = navigationStack.pop();
...

Of course, this works if your navigation from view3 to view2 if akin to a "back" action. If it is a deliberate navigation to any page that may have been previously initialized, you are bound to save your pages' states in a hash, indexed by page name (or full route).

Your case should fit one of these two models. If it doesn't, either your requirements are not well defined enought, or I misunderstood your question.

thibauts
  • 1,638
  • 9
  • 8
1

thibauts' answer seems in the right direction. Do give that a read.

The way I understand your problem. at any time opening a "page3a" depends on the previous page. if it was "page2a" it will work in a certain way. if it was "page2b" it will work differently. And this information you keep in tempData for every page.

This problem will not be solved by backbone routes. As they just provides means of converting URL routes into a execution sequence. You will still have to manage tempData. Solution in the end depends on the size of tempData and how you manage it. Is there some shared data which can be taken out of tempData structure and kept in a shared structure. Is there some data which can be kept on its own, changes very rarely and can implements some kind of memento pattern.

there are three cases -

  1. tempData is relatively small and you can keep some copies of it - in this case you can keep fixed history [say last 10 pages] of tempData in a circular buffer. Keep on deleting oldest items and adding new one at every page visit. This will give you both back navigation as thibauts said and history jump along with forward navigation after back Navigation if the user haven't changed anything and specifically try forward button/nav.

    1.1 EDIT : In case you wish to retain a forward action which has already been backed, you can still keep it in the circular queue, but this way you have to distinguish between the two visits of view2 (see comments). and consider everything as a forward move and push in history (circular queue). another way will be to keep a pointer (array index. reference if you may) to the current page in the buffer and when adding the new page (on some action) to buffer move the pointer the that one. when doing a back/forward moves just move the point in buffer and render. [If it is too complex or costly to implement, you can completely ignore a view/page once user has moved back from it. simply remove it from history so user cant use fwd again. or back twice to reach it after back-new-back-back. backbone.router has a way of history replace]

  2. tempData is large enough that you can keep only few copies of it [lets assume less than 5]. In this case keep one copy for the previous page. and keep copies of specific key pages in history where there's high chance of user returning. for example in a shopping flow, the user may return back to cart more often than the address page. You will need a special data structure for this, where you can maintain priority of every page. priority is based on the time and some biasing criteria. the newest gets high time parameter and the most important gets high biasing parameter. whenever you visit a new page. remove the tempData of the lowest priority.

  3. tempData is of monstrous size - give up on the back navigation. keep few points in history which works like reset points (if possible) and where you can recreate the tempData from scratch.

If there's any other bottle neck than the tempData. Please comment.

Community
  • 1
  • 1
Mohit
  • 2,239
  • 19
  • 30
  • Thank you for your comment. Actually yes I agree that thibatus is in the right way but more complex. If you follow the browser's back and prev buttons you can see the stack. And the way the browser implements : view 3 -> view 2 (view 3 context is not deleted and stills in the stack) then if view 3 -> back view2 -> view 4 : it deletes the context of view 3. That's the optimisation I'm looking for. Didn't found any js library for that – Houcem Berrayana Oct 10 '13 at 08:04
  • you mean first you reach to view2 thn move to view3a. (view2 -> view3a). now back. (view3a->view2) and thn a different fwd (view2 -> view3b) and this way you loose view3a? – Mohit Oct 10 '13 at 09:38
  • Yes This is how I think memory should be handled. This way I can ensure that my context is synced with the history stack – Houcem Berrayana Oct 10 '13 at 11:05
  • 1
    i have added an edit. take a look. and as @thibauts said about architecture problem. you should reconsider the structure first before going into workarounds. – Mohit Oct 10 '13 at 19:52
  • Yes, that makes sense that's why I wanted to ask this question before going too far in the code as I plan to develop phase II and phase III – Houcem Berrayana Oct 12 '13 at 15:29