Garbage collection and <form>s loaded with Ajax

39 views
Skip to first unread message

Magnus Lindberg

unread,
Nov 30, 2010, 10:02:52 PM11/30/10
to Lift
Hi Lift people!

A question related to Lift's garbage collection, <form>:s and Ajax.

In my Web app, there's a page with lots of messages. It's the
Discussion Page.
When a user clicks a Reply link on that page,
another html page is generated, with a Reply form,
and Lift's magic form <input> names (e.g. "F352598235789235").

The Web app, however, issues the Reply request via Ajax.
It won't show the Reply page at all --- instead it extracts the <form>
element from the
Reply html page, and inserts the <form> into the Discussion Page.

So the Reply page itself never appears in a browser tab on its own.

This means this part of the Reply page is lost: (and not executed at
all)

------------
<script type="text/javascript">
// <![CDATA[
jQuery(document).ready(function()
{liftAjax.lift_successRegisterGC();});
var lift_page = "F11636420282974CQ";
// ]]>
</script></body>
------------

Which means the form <input>:s will be garbage collected after a
while.
(Correct?)

I suppose I could use Lift's Ajax form functionality instead of
generating a separate Reply HTML page.
Then no <input> elems would be prematurely GC:ed. (Correct?)

However:

1) I'd prefer generating that separate Reply form Web page,
so the Web site work with JavaScript disabled. (Then the reply link
would
open the Reply html page.)

2) I'd prefer not to implement both an Ajax form (in case JavaScript
is enabled)
and a plain html page with a POST <form> (in case JavaScript is
disabled).

Do you have any suggestion on what I could do?

(Disabling GC would solve my problem but I know it's no good
solution.)


Thanks, Magnus

Naftoli Gugenheim

unread,
Dec 1, 2010, 4:11:43 PM12/1/10
to liftweb


On Tue, Nov 30, 2010 at 10:02 PM, Magnus Lindberg <lindber...@gmail.com> wrote:
instead it extracts the <form>
element from the
Reply html page,

You extract it via Javascript? Why not have a template that has the ajax subset of the static Reply page as its entire contents? Then I think it would include the javascript just like any page.

Also there should be a way to specifically ask for that javascript GC snippet but I'm not sure how...

David Pollak

unread,
Dec 1, 2010, 6:01:10 PM12/1/10
to lif...@googlegroups.com
Why not compose the page on the server?  If you request the HTML to overlay on your page via a Lift Ajax call, the new page will be composed in the context of the page that the Ajax request came from and will be properly GCed when the rest of the page's functions are GCed.

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Blog: http://goodstuff.im
Surf the harmonics

Magnus Lindberg

unread,
Dec 2, 2010, 12:04:15 PM12/2/10
to Lift

Hi Naftoli and David! Thanks for your input.

(I'm not sure I've understood you correctly. I'm very new to Lift.)

Naftoli Gugenheim wrote:
> On Tue, Nov 30, 2010 at 10:02 PM, Magnus Lindberg <lindberg.mag...@gmail.com 
> > wrote: 
> > instead it extracts the <form> 
> > element from the 
> > Reply html page, 
>
> You extract it via Javascript? Why not have a template that has the ajax 
> subset of the static Reply page as its entire contents? Then I think it 
> would include the javascript just like any page. 

Then I'd need both a static Reply page (for people with no JavaScript)
and an Ajax version of the Reply page?
-- That'd be twice as much code and stuff to test? (That's why
I'd prefer having only one non-ajax Reply form and nothing else.)

Anyway, I suppose it's a good solution (with an Ajax version too),
since the static HTML Reply form costs more bandwidth.

***

> David Pollak wrote:
> Why not compose the page on the server? If you request the HTML to overlay
> on your page via a Lift Ajax call, the new page will be composed in the
> context of the page that the Ajax request came from and will be properly
> GCed when the rest of the page's functions are GCed.

That means I'd construct the Reply stuff with calls to
SHtml.ajaxCall ?
Then, if I also want the static Reply form page (for people without
JavaScript),
I'd need two implementations?

I suppose it's the best approach performance/bandwidth wise,
with both a static page and an Ajax version.


***

Perhaps this is another possible solution:
(No server side state, no timeouts, (almost) no XSRF attacks.)

When the client user clicks Reply, the server replies with a
*stateless* plain HTML reply form.
(With <input> names mapped to *no* server-side-functions.)
Which the client inserts into the main Discussion Page (using jQuery).

The user then enters a reply, and clicks a certain "Preview" button,
and the client POSTs the form to the server, which saves [the preview
of the reply]
in the database, and assigns it a unique GUID.
(XSRF attacks possible, but the preview is not published so perhaps
this is acceptable.)

The server replies with the HTML form, which now includes the
preview and the GUID.

The client extracts the GUID from the reply (with jQuery).
Later, when the user clicks "Publish reply",
the client uploads the GUID to the server, which then publishes the
post.

Since all forms are stateless, they won't time out.

And since the server generates the GUID,
no one but the client can ask the server to *publish* the preview
(no XSRF attacks).


Kind regards, Magnus

David Pollak

unread,
Dec 2, 2010, 12:48:12 PM12/2/10
to lif...@googlegroups.com
On Thu, Dec 2, 2010 at 9:04 AM, Magnus Lindberg <lindber...@gmail.com> wrote:

Hi Naftoli and David! Thanks for your input.

(I'm not sure I've understood you correctly. I'm very new to Lift.)

Naftoli Gugenheim wrote:
> On Tue, Nov 30, 2010 at 10:02 PM, Magnus Lindberg <lindberg.mag...@gmail.com 
> > wrote: 
> > instead it extracts the <form> 
> > element from the 
> > Reply html page, 
>
> You extract it via Javascript? Why not have a template that has the ajax 
> subset of the static Reply page as its entire contents? Then I think it 
> would include the javascript just like any page. 

Then I'd need both a static Reply page (for people with no JavaScript)
and an Ajax version of the Reply page?
-- That'd be twice as much code and stuff to test? (That's why
I'd prefer having only one non-ajax Reply form and nothing else.)

Please take a look at http://demo.liftweb.net/templating/embed

Using <embed> you can place your form in a single file, embed it in the static HTML page and use S.runTemplate(List("templates-hidden", "my_page")) in the service.
 

Anyway, I suppose it's a good solution (with an Ajax version too),
since the static HTML Reply form costs more bandwidth.

***

> David Pollak wrote:
> Why not compose the page on the server?  If you request the HTML to overlay
> on your page via a Lift Ajax call, the new page will be composed in the
> context of the page that the Ajax request came from and will be properly
> GCed when the rest of the page's functions are GCed.

That means I'd construct the Reply stuff with calls to
SHtml.ajaxCall ?
Then, if I also want the static Reply form page (for people without
JavaScript),
I'd need two implementations?

No.  You can make a determination as to Ajax vs. non-Ajax in your template.  For example, <lift:embed what="/templates-hidden/my_page" no_ajax="true"/>

Then in your Ajax form, you can determine (in your own snippet) whether to build the form:

<lift:form.ajax>form here</lift:form.ajax>
vs.
<lift:form.post>form here</lift:form.post>


 

I suppose it's the best approach performance/bandwidth wise,
with both a static page and an Ajax version.


***

Perhaps this is another possible solution:
(No server side state, no timeouts, (almost) no XSRF attacks.)

When the client user clicks Reply, the server replies with a
*stateless* plain HTML reply form.
(With <input> names mapped to *no* server-side-functions.)
Which the client inserts into the main Discussion Page (using jQuery).

The user then enters a reply, and clicks a certain "Preview" button,
and the client POSTs the form to the server, which saves [the preview
of the reply]
in the database, and assigns it a unique GUID.
(XSRF attacks possible, but the preview is not published so perhaps
this is acceptable.)

I think you're over-thinking the issue.  It's much easier to use Lift's templating system to render the templates than making some weird HTTP call back into Lift to generate a page, then strip markup from it then ship it over the wire.
 

The server replies with the HTML form, which now includes the
preview and the GUID.

The client extracts the GUID from the reply (with jQuery).
Later, when the user clicks "Publish reply",
the client uploads the GUID to the server, which then publishes the
post.

Since all forms are stateless, they won't time out.

And since the server generates the GUID,
no one but the client can ask the server to *publish* the preview
(no XSRF attacks).


Kind regards, Magnus
--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Magnus Lindberg

unread,
Dec 5, 2010, 5:12:36 PM12/5/10
to Lift
Hi David!


On Dec 2, 6:48 pm, David Pollak <feeder.of.the.be...@gmail.com> wrote:
> On Thu, Dec 2, 2010 at 9:04 AM, Magnus Lindberg wrote:
>
>
> > Hi Naftoli and David! Thanks for your input.
>
> > (I'm not sure I've understood you correctly. I'm very new to Lift.)
>
> > Naftoli Gugenheim wrote:
> > > On Tue, Nov 30, 2010 at 10:02 PM, Magnus Lindberg <
> > lindberg.mag...@gmail.com
> > > > wrote:
> > > > instead it extracts the <form>
> > > > element from the
> > > > Reply html page,
>
> > > You extract it via Javascript? Why not have a template that has the ajax
> > > subset of the static Reply page as its entire contents? Then I think it
> > > would include the javascript just like any page.
>
> > Then I'd need both a static Reply page (for people with no JavaScript)
> > and an Ajax version of the Reply page?
> > -- That'd be twice as much code and stuff to test? (That's why
> > I'd prefer having only one non-ajax Reply form and nothing else.)
>
> Please take a look athttp://demo.liftweb.net/templating/embed
>
> Using <embed> you can place your form in a single file, embed it in the
> static HTML page and use S.runTemplate(List("templates-hidden", "my_page"))
> in the service.
>

:-) Seems that's exactly what I needed.
I added something fairly similar to this:

object StatefulRest extends RestHelper {
serve {
case "v0"::"id"::path XmlGet _ =>
S.runTemplate("v0"::"reply"::Nil).open_!.head
}
}

and now Lift-Web constructs a <form> that seems to belong to
the page from which the request was made,
instead of a completely new and separate Web page.
(So I suppose the <form> won't be garbage collected prematurely.)

(And I still have that separate [plain old static HTML page] version
of the <form>, without duplicating any code.)

Thanks, Magnus

Ján Raška

unread,
Oct 18, 2011, 6:31:52 PM10/18/11
to lif...@googlegroups.com
Hi David,

I've run into a similar issue and found a solution here, so everything works now. I'm however curious to know, why the form is not properly GCed if I have it in a template within a SiteMap and run $('#component').load(/my-sitemap/template) from a javascript, but it works perfectly if I setup a RestHelper:

case "rest"::"template"::Nil Post _ => S.runTemplate("my-sitemap"::"template"::Nil).open_!.head 

and then run from javascript $('#component').load(/rest/template)

I was under assumption that SiteMap under the hood does pretty much the same thing, so I was quite surprised that if I do it with Rest, it'll be GCed properly.

Thanks

Jan

On Thursday, December 2, 2010 6:48:12 PM UTC+1, David Pollak wrote:
On Thu, Dec 2, 2010 at 9:04 AM, Magnus Lindberg <lindber...@gmail.com> wrote:

Hi Naftoli and David! Thanks for your input.

(I'm not sure I've understood you correctly. I'm very new to Lift.)

Naftoli Gugenheim wrote:
> On Tue, Nov 30, 2010 at 10:02 PM, Magnus Lindberg <lindber...@gmail.com 

David Pollak

unread,
Oct 18, 2011, 8:20:16 PM10/18/11
to lif...@googlegroups.com
If the template is part of a full page load (something that's rendered as an HTML page rather than as a REST call and stuff that's in SiteMap is rendered as a full HTML page), then the results will be garbage collected if there's no heartbeat from the full page load.  Thus, if you put the page in SiteMap and have it rendered, the rendering will contain heartbeat code (if there's a <body> tag in the thing rendered and there's a callback function generated.)  But there will be no heartbeat actually generated because the code will be inserted into a page that has its own heartbeat id.  And after 10 minutes, the closures will be GCed.

On the other hand, if the rendering is part of an REST call, then the closures generated during the REST call will never be GCed and the form will be valid for the lifespan of the session in which they were created.

But in general, it's probably best to use Lift's Ajax mechanism to create forms that are associated with a page because those calls will do the right thing in terms of associating the closures with the page that the Ajax calls came from.




--
Lift, the simply functional web framework http://liftweb.net

Ján Raska

unread,
Oct 19, 2011, 6:52:23 AM10/19/11
to lif...@googlegroups.com
Thanks a lot David for the explanation, I understand it now very well. I use Lift Ajax wherever it's possible, but in this case I couldn't as this call is done by external jQuery library.

Jan
Reply all
Reply to author
Forward
0 new messages