It could be more generic I think... we need to abstract out:
1. persistence management
2. additional writing of hidden fields
3. doing a post instead of a get for /all/ components that trigger round trips in a generic way.

Btw, you're welcome to come with modifications of the original Wicket source, as that is what we need in the end. I'll try to think with you on this. Chris, if you're reading this and you have time (changing the daipers currently ?), I'd like to know what the general plan was you had in mind so far.

Eelco


Phil Kulak wrote:

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;
        }
}



-------------------------------------------------------
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

Reply via email to