Tabbed browsing and "session" state

36 views
Skip to first unread message

tiro

unread,
Sep 26, 2009, 11:38:49 AM9/26/09
to Lift
Hi,
has anyone investigated or built a way to support tabbed browsing when
there is considerable view/workflow state? Or have I missed an elegant
way for this to be done in Lift?

If I am not mistaken, Lift currently supports
1. Sending continuations (things that will need to be done) from one
Request to the next in the form of FuncHolders that are attached to an
ID that is unique for a sent page. This works perfectly in a tabbed
environment, and you can send state this way, but the state doesn't
live on in the response sent by the snippet that has received it.
2. Session variables that are global to the session, as well as
3. Stateful snippets that are instantiated at most once in each
session.
When assuming that your user knows how to browse tabbed, I can
currently not imagine what 2. and 3. could be practically useful for.
Very often though, it seems, people use these mechanisms and then
later realize the mess they are in.

E.g. (quite funny) http://www.highdots.com/forums/javascript/browser-tab-269390.html
More serious:
http://www.codeodor.com/index.cfm/2007/7/19/Why-tabbed-browsing-makes-holding-form-state-with-sessions-obsolete/1470

A simple example from an application I'm developing, and I'll bet more
than half of all Web apps, is this: I have one page where users can
search for a bunch of documents in a database (search form and result
list are on the same page - let's not make it too complicated).
Clicking on a result will navigate to the detail view of a document. I
am sure sooner or later the customer will want a link on the detail
view to go back to the search he came from.

If I put the last search parameters used into Session Vars, this will
not work if the customer uses several such result lists in parallel
(not unrealistic in this case). He will always end up going back to
the last search used, not the one that was used to open the detail
view. Very bad behavior for example when the user has edited an item,
and it then "vanishes" from his view.

What would work is: put the search parameters into hidden fields and/
or URL parameters of the detail view. But you break your fingers doing
that and it gets really messy when the detail view can be opened from
several different context, and the conversation that takes you back to
the original context has several steps. There is also a limit to what
you can put into a URL, I believe. You use a framework because it
saves you these kinds of headaches.

It seems to me, Lift already has 80% of what it takes for a great
solution. It is the mechanism mentioned under 1. Any tab that is still
open calls home regularly to say "I'm still here, don't forget me".
This is good because otherwise we would have to consider any page ever
sent to the client to still be open and come back to haunt us,
demanding us to behave as if nothing had happened since. The existing
mechanism would only need to allow the state to be passed on, with a
delta, to the next response-request episode.






Naftoli Gugenheim

unread,
Sep 27, 2009, 12:09:33 AM9/27/09
to lif...@googlegroups.com
I think what you want is StatefulSnippet. They're lifetime is not the session--it's as long as you keep it alive.
Basically Lift maintains a registry of stateful snippets in each request. When a snippet is needed (referenced in a template), if an instance exists in the registry that instance is used; otherwise a new one is instantiated and registered. Thus if it's referenced in two places on one page, this same instance will be reused.
If you want the same instance to be used in a future page, ultimately what has to happen is that a callback that will be executed in the future request must have a reference to the old snippet instance, and register it. You can effect this manually by calling registerThisSnippet on the snippet in say a hidden field. StatefulSnippet has a link method that's like SHtml.link but it calls registerThisSnippet. Also a submit button (at least SHtml.submit) that goes to the same page maintans the same snippet instance; I forgot how.
But without keeping the StatefulSnippet alive, the instance will not be preserved; and by the same token, you can keep several stateful snippets alive, as long as it's one per request.
However this only works when the orignal instance was provided by lift. In order for registerThisSnippet to work, lift has to have provided the snippet with a name.
An alternative solution is S.mapSnippet. This allows you to short circuit the snippet lookup mechanism for a single request (at a time). You specify a snippet name, exactly as it occurs in the template, not including "lift:", and a function. If you have a snippet instance you can pass its snippet method as the function here. Of course, the snippet method can call mapSnippet itself.
So each search results link can have a callback function that sets up a snippet corresponding to that link, and calls mapSnippet so that that instance's snippet functions will be used in the page linked to.


-------------------------------------

marius d.

unread,
Sep 27, 2009, 12:22:09 AM9/27/09
to Lift


On Sep 26, 10:38 am, tiro <tim.romb...@googlemail.com> wrote:
> Hi,
> has anyone investigated or built a way to support tabbed browsing when
> there is considerable view/workflow state? Or have I missed an elegant
> way for this to be done in Lift?

There is a rather recent wizard code in lift that Dave added (and I
haven't looked on it yet :) ... ) that could potentially apply here?

>
> If I am not mistaken, Lift currently supports
>  1. Sending continuations (things that will need to be done) from one
> Request to the next in the form of FuncHolders that are attached to an
> ID that is unique for a sent page. This works perfectly in a tabbed
> environment, and you can send state this way, but the state doesn't
> live on in the response sent by the snippet that has received it.

No your function that is called upon a request, receives the data.
That data needs to be preserved into a RequestVar, SessionVar or into
a StatefulSnippet so that when rendering phase starts that value is
visible by the snippet itself.

Function ID-s are generated for each page rendering. This is important
for security reasons.

>  2. Session variables that are global to the session, as well as
>  3. Stateful snippets that are instantiated at most once in each
> session.

Not necessarily. You can preserve a StatefulSnippet's reference if
you're using the functions exposed by it such as link or redirectTo.
Also using a StatefulSnippet as a form preserves its reference.
Otherwise the StatefulSnippet reference is not kept in LiftSession.

> When assuming that your user knows how to browse tabbed, I can
> currently not imagine what 2. and 3. could be practically useful for.
> Very often though, it seems, people use these mechanisms and then
> later realize the mess they are in.
>
> E.g. (quite funny)http://www.highdots.com/forums/javascript/browser-tab-269390.html
> More serious:http://www.codeodor.com/index.cfm/2007/7/19/Why-tabbed-browsing-makes...
>
> A simple example from an application I'm developing, and I'll bet more
> than half of all Web apps, is this: I have one page where users can
> search for a bunch of documents in a database (search form and result
> list are on the same page - let's not make it too complicated).
> Clicking on a result will navigate to the detail view of a document. I
> am sure sooner or later the customer will want a link on the detail
> view to go back to the search he came from.
>
> If I put the last search parameters used into Session Vars, this will
> not work if the customer uses several such result lists in parallel
> (not unrealistic in this case). He will always end up going back to
> the last search used, not the one that was used to open the detail
> view. Very bad behavior for example when the user has edited an item,
> and it then "vanishes" from his view.
>
> What would work is: put the search parameters into hidden fields and/
> or URL parameters of the detail view. But you break your fingers doing
> that and it gets really messy when the detail view can be opened from
> several different context, and the conversation that takes you back to
> the original context has several steps. There is also a limit to what
> you can put into a URL, I believe. You use a framework because it
> saves you these kinds of headaches.
>
> It seems to me, Lift already has 80% of what it takes for a great
> solution. It is the mechanism mentioned under 1. Any tab that is still
> open calls home regularly to say "I'm still here, don't forget me".

Depends how you are building your tabs. If your tab is a totally
different page than the state should be preserved on the server and
the dedicated place is a SessionVar. But you could build your tabs as
being part of the same page, hence changing the tabs becomes a matter
of making divs visible/hidden and this implies not server hits. Just
JavaScript. This will allow you to navigate from one tab to another
pretty fast => a better user experience.

tiro

unread,
Sep 27, 2009, 2:33:34 AM9/27/09
to Lift

Naftoli,
thanks for providing these insights into the inner workings of
Stateful Snippets. The mapSnippet solution sounds interesting. I knew
that snippets don't live on when not needed, but assumed that IF one
is alive in the current session, you would always get that one
instance. Hence at most one instance would be alive per snippet class
and per session. What you seem to indicate is that the lifecycle is
held together by redirects and such (and therefore page ids) rather
than two requests being in the same session. I will experiment how
that works with tabs. I guess one has to still be very careful since
the user may go from the view initiating a snippet and open several
tabs from there, whereafter you could still get unwanted interference
between the tabs if you don't pay attention.


Marius, thanks for your points
>Wizard code
exciting.. since the wizardiness probably won't jump into my eye, if
you happen to stumble on it in the next few days, could you perhaps
post a classname here?

>That data needs to be preserved into a RequestVar,
That is precisely what I meant by "passing state on from a response to
the next request" :-) RequestVars are benign.

>AJAX Tabs
Had briefly thought of that. Makes good sense with some types of apps.
In this particular app I've already made other compromises to preserve
standard behavior and let the user bookmark the current URL etc.
Actually things would already be much simpler if browsers allowed you
to address tabs by Javascript ("close current tab", jump to "search
results" tab). One could then keep the results list open in a user-
friendly way. But apparently browsers have been getting quite
restrictive on that, for obvious reasons.

marius d.

unread,
Sep 27, 2009, 11:10:43 AM9/27/09
to Lift
Tiro,

Please see the lift-wizard project.

Br's,
Marius
Bookmarking URL's would make you loose state unless you preserve that
state in the URL itself. But you're fully aware of this.

Timothy Perrett

unread,
Sep 27, 2009, 11:19:32 AM9/27/09
to lif...@googlegroups.com

Please bear in mind that this is *first pass* code and nothing
production ready.

Cheers, Tim

Naftoli Gugenheim

unread,
Sep 27, 2009, 11:31:24 AM9/27/09
to lif...@googlegroups.com
Do you mean multiple browser tabs? They won't interfere, because which stateful snippet is used in a given request depends on which is registered in that request which depends on which function is called which depends on which function id is in the request query parameters which depends on the link that was clicked which is generated by the stateful snippet.... :)
But if one page must link to several pages each with a pre-instantiated snippet you must use mapSnippet.
Marius, did David work on lift-wizard since the last time he said he doesn't know when he will find time? Also, how would he approach his goal using lift-wizard?


-------------------------------------
marius d.<marius...@gmail.com> wrote:


Tiro,

Please see the lift-wizard project.

marius d.

unread,
Sep 28, 2009, 10:39:55 AM9/28/09
to Lift


On Sep 27, 10:31 am, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> Do you mean multiple browser tabs? They won't interfere, because which stateful snippet is used in a given request depends on which is registered in that request which depends on which function is called which depends on which function id is in the request query parameters which depends on the link that was clicked which is generated by the stateful snippet.... :)
> But if one page must link to several pages each with a pre-instantiated snippet you must use mapSnippet.
> Marius, did David work on lift-wizard since the last time he said he doesn't know when he will find time? Also, how would he approach his goal using lift-wizard?

Dunno. I pointed to lift-wizard mostly for looking to the code and see
if the concept fits tiro's needs.


>
> -------------------------------------
Reply all
Reply to author
Forward
0 new messages