I just finished getting my POC working. It's three classes (the two
new ones are attached) with no modifications to the original Wicket
source. It could probably be a lot more elegant otherwise, but I was
mainly concerned with weather my approach was even possible. Basicly,
you just set your IPageFactory to StatelessPageFactory and wrap any
controls that you want maintained on the client in a StatelessForm,
which handles adding JavaScript to links for form submission and the
hidden fields. It's kind of cool to take a complicated sorted, paging
list, click it a bunch of times, restart the server, click refresh,
and have it pop right back exactly how you left it. :)
-Phil
On 7/1/05, Phil Kulak <[EMAIL PROTECTED]> wrote:
> Johan, I think you're making it a bit harder then it needs to be. I've
> actually got it working (depending on how you define "working") on my
> test machine right now with just that serialization class and a
> subclass of form. Since a form has easy access to it's page and all
> it's components, it can add javascript to every link it finds for
> onClick submits, serialize its page on render on right it to a hidden
> field, unserialize the page on beginRequest, and find it's submitting
> link on validate to call onClick(). Granted, it would probably be nice
> to move some of these things out of the form, page unserialization
> probably, but it can be pretty simple.
>
> The only problem I'm having is that I have to use an
> IFormSubmitListener, which means that while the page IS restored from
> the client, it's first taken from the session, which doesn't really
> make any sense. I haven't figured out the "right" way to create a new
> page on every request. I'll probably take a look at how bookmarkable
> pages work.
>
> -Phil
>
> On 7/1/05, Eelco Hillenius <[EMAIL PROTECTED]> wrote:
> > Your mailbox is full. We (Martijn and I) keep getting warnings from your
> > box.
> >
> > Eelco
> >
> > Johan Compagner wrote:
> >
> > > i didn't see the patch yet (where is that mail?? sourceforge problem?)
> >
> >
> >
> >
> > -------------------------------------------------------
> > SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
> > from IBM. Find simple to follow Roadmaps, straightforward articles,
> > informative Webcasts and more! Get everything you need to get up to
> > speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
> > _______________________________________________
> > Wicket-develop mailing list
> > [email protected]
> > https://lists.sourceforge.net/lists/listinfo/wicket-develop
> >
>
package com.apropobenefits.wicket;
import wicket.AttributeModifier;
import wicket.Component;
import wicket.markup.ComponentTag;
import wicket.markup.MarkupStream;
import wicket.markup.html.form.Form;
import wicket.markup.html.link.Link;
import wicket.markup.parser.XmlTag;
import wicket.model.Model;
public class StatelessForm extends Form {
public StatelessForm(final String id) {
super(id);
}
/**
* Adds an ID and sets the action of the form to a bookmarkable link.
*/
@Override
protected void onComponentTag(final ComponentTag tag) {
super.onComponentTag(tag);
tag.put("action", getPage().urlFor(null, getPage().getClass(), null));
tag.put("id", "theForm");
}
/**
* Adds some javascript to every link within this form. As well as a
* couple hidden input fields.
*/
@Override
protected void onComponentTagBody(MarkupStream markupStream,
ComponentTag openTag) {
// This gives every link a JavaScript onClick() method and wipes out
// its origonal href attribute. It would probably be better if the links
// themselves took care of thier own rendering, but this was the quickest
// solution.
this.visitChildren(Link.class, new IVisitor() {
public Object component(Component component) {
String script =
"document.getElementById('submittingLink').value = '" +
component.getPageRelativePath() + "';" +
"document.getElementById('theForm').submit();" +
"return false";
component.add(new AttributeModifier("onClick", true,
new Model(script)));
component.add(new AttributeModifier("href", true,
new Model("#")));
return CONTINUE_TRAVERSAL;
}
});
// The pageState hidden field.
XmlTag psInput = new XmlTag();
psInput.setType(XmlTag.OPEN_CLOSE);
psInput.setName("input");
psInput.put("type", "hidden");
psInput.put("name", PageState.REQUEST_KEY);
psInput.put("value", new PageState(getPage()).getString());
renderComponentTag(new ComponentTag(psInput));
// The field that gets populated with the path to the submitting link.
XmlTag subLink = new XmlTag();
subLink.setType(XmlTag.OPEN_CLOSE);
subLink.setName("input");
subLink.put("type", "hidden");
subLink.put("name", "submittingLink");
subLink.put("id", "submittingLink");
renderComponentTag(new ComponentTag(subLink));
super.onComponentTagBody(markupStream, openTag);
}
/**
* Called by the page factory just after population.
*/
protected void onLinkSubmit() {
Link link = findSubmittingLink();
if (link != null) {
link.onClick();
}
}
/**
* Returns the link that caused this form to be submitted or null
* if it wasn't submitted with a link.
*/
private Link findSubmittingLink() {
final String path = getRequest().getParameter("submittingLink");
return (Link) visitChildren(Link.class, new IVisitor() {
public Object component(final Component component) {
if (component.getPageRelativePath().equals(path)) {
return component;
}
return CONTINUE_TRAVERSAL;
}
});
}
}
package com.apropobenefits.wicket;
import wicket.Component;
import wicket.DefaultPageFactory;
import wicket.IPageFactory;
import wicket.Page;
import wicket.PageParameters;
import wicket.Request;
import wicket.RequestCycle;
import wicket.Component.IVisitor;
/**
* A page factory that takes care of state management.
*
* @author Phil Kulak
*/
public class StatelessPageFactory implements IPageFactory {
private IPageFactory internalFactory = new DefaultPageFactory();
public Page newPage(Class c, PageParameters params) {
return populatePage(internalFactory.newPage(c, params));
}
public Page newPage(Class c) {
return populatePage(internalFactory.newPage(c));
}
private Page populatePage(Page page) {
Request request = RequestCycle.get().getRequest();
String ser = request.getParameter(PageState.REQUEST_KEY);
if (ser != null) {
// Return the page to its origonal state.
new PageState(ser).populatePage(page);
// Find the form that submitted. Actually, find the FIRST stateless
// form. If there's two forms you're out of luck.
page.visitChildren(StatelessForm.class, new IVisitor() {
public Object component(Component component) {
((StatelessForm) component).onLinkSubmit();
return STOP_TRAVERSAL;
}
});
return page;
}
return page;
}
}