uklance / tapestry-stitch

Sample components and concepts in Tapestry 5
MIT License
14 stars 7 forks source link

Tabs are problems in forms #6

Open nfranke opened 11 years ago

nfranke commented 11 years ago

If you enclose a tab group in a form a random element is created for some reason.

Submitting the form loses the current tab group as well. In fact, the current tab is lost almost all the time unless one @Persist the activeTab property. Note sure if anything can be done about that, though.

Also it's pretty common to do initialization in onActivate, but that won't work with tabs since the active tab is set after onActivate is called. At least document it, I guess.

uklance commented 11 years ago

Can you view the source and tell me what the random element is? Take a look at the generated HTML. This is strange.

I have thought about having an ajax="false" option so that tab changes cause a full page refresh. Would that help you? Then you could use a @PageActivationContext which would remain through the posting of the form.

You can test this by setting the active tab to a @PageActivationContext and then right click on a tab and open in new window. I'm guessing if you do this, the active tab will remain though the form post.

nfranke commented 11 years ago

The editor cut out my text. It's an empty input element. I can' easily run your demo, but try putting a t:form around the tab group in your demo. It's in the generated HTML. I can't figure out where it's coming from. I eliminated it by putting the form inside the Tab element.

Disabling AJAX would help some things, I think. I managed to get it working with the @Persist, but I'm not sure if I like that solution. I have a grid in there so stuff is stored in the session anyway.

uklance commented 11 years ago

Ok, how's this for a complex solution. You could add a listener on your page for onTabChange(tabindex) which should "bubble up" from the tabgroup to your page

In the event, use the ajax response renderer to update a hidden field in your form

Then when your form posts, you will have the active tab which you can pass to your onactivate

uklance commented 11 years ago

By the way, the tab group never adds an input. I wonder if it's the form component adding t:formdata

nfranke commented 11 years ago

Were you able to reproduce it by inserting a t:form around your example?

nawa commented 10 years ago

I have the same issue. It is no t:formdata hidden input, it is empty <input>. Html inside form looks like

    <form id="form" method="post" action="/page.form"
        onsubmit="javascript:return Tapestry.waitForPage(event);">
        <div class="" style="display: none;">
            <input type="hidden" name="t:formdata"
                value="HoIF+WNTWJhH80N9cnOkgjFGj0s=:H4sIAAAAAAAAAFvzloEVAN3OqfcEAAAA">
        </div>
        <div id="tabsZone" class="t-zone tapestry-zone">
            <ul class="nav nav-tabs">
                <li class="active"><a href="/page.tabgroup:tabchange/tab1"
                    onclick="javascript:return Tapestry.waitForPage(event);"
                    id="eventlink">tab1</a></li>
                <li><a href="/page.tabgroup:tabchange/tab2"
                    onclick="javascript:return Tapestry.waitForPage(event);"
                    id="eventlink_0">tab2</a></li>
            </ul>
            <input> tab1Content
        </div>
    </form>
uklance commented 10 years ago

In it's current state, the tabgroup cannot be used inside a form.

The easiest solution is to add a ajax="false" option which causes all tabs to be fetched and rendered on page load but only one is visible. With this option, a tab change would not cause an ajax request and would simply toggle the visible tab on the client. This has the added benefit that field changes would not be lost when changing tabs.

To do ajax tab changes in a form would be much more difficult. This would probably need to use FormInjector and you'd also need to record the field changes somewhere before updating the zone (session / cookie / js variable etc).

nawa commented 10 years ago

I made workaround for me in Tab.java. It ugly but works for my needs

    @AfterRenderBody
    void afterRender(MarkupWriter writer) {
        if (renderBody) {
            // capture the body markup and remove it from the DOM, it will be rendered by the TabGroup
            Element bodyWrapper = writer.getElement();
            List<Node> children = bodyWrapper.getChildren();
            //workaround. Empty input if tabGroup inside form
            if (children.size() > 1 && children.get(0) instanceof Element) {
                Element element = ((Element) children.get(0));
                if ("input".equals(element.getName()) && element.getAttributes().size() == 0 && element.getChildren().size() == 0) {
                    element.remove();
                }
            }
            //***workaround
            writer.end();
            TabModel tabModel = (TabModel) request.getAttribute(TabGroup.ATTRIBUTE_TAB_MODEL);
            tabModel.setActiveTabBody(bodyWrapper.getChildMarkup());
            bodyWrapper.remove();
        }
    }
uklance commented 10 years ago

You will still have a problem with two tabs on a form when each tab has fields. Loading a second tab will wipe the field values in the first tab.