SubmitContext Component based on Tapestry 5.1

Source:

(Modified from T5.1.0.5. Put it into yourapp.components package.)

//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yourapp.components;

import org.apache.tapestry5.*;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Events;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.SupportsInformalParameters;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.FormSupport;
import org.apache.tapestry5.services.Heartbeat;
import org.apache.tapestry5.services.Request;

/**
 * Corresponds to <input type="submit">, a client-side element that can force the
 * enclosing form to submit. The submit responsible for the form submission will post a
 * notification that allows the application to know that it was the responsible entity. The
 * notification is named "selected" and has a String context. The major difference between this
 * component and Tapestry's Submit component is that this component's context is immutable and
 * can, for example, be set to different values within a loop.
 */
@SupportsInformalParameters
@Events(EventConstants.SELECTED + " by default, may be overridden")
public final class SubmitContext implements ClientElement {
    /**
     * If true, then any notification sent by the component will be deferred until the end of
     * the form submission (this is usually desirable).
     */
    @Parameter
    private boolean defer = true;

    /**
     * The name of the event that will be triggered if this component is the cause of the form submission. The default
     * is "selected".
     */
    @Parameter(allowNull = false, defaultPrefix = BindingConstants.LITERAL)
    private String event = EventConstants.SELECTED;

    /**
     * If true, then the field will render out with a disabled attribute (to turn off client-side behavior). Further, a
     * disabled field ignores any value in the request when the form is submitted.
     */
    @Parameter("false")
    private boolean disabled;

    /**
     * The value that will be made available to event handler method of this component when the form is
     * submitted.
     */
    @Parameter
    private String context;

    /**
     * If provided, the component renders an input tag with type "image". Otherwise "submit".
     */
    @Parameter(defaultPrefix = BindingConstants.ASSET)
    private Asset image;

    @Environmental
    private FormSupport formSupport;

    @Environmental
    private Heartbeat heartbeat;

    @Inject
    private ComponentResources resources;

    @Inject
    private Request request;

    @Inject
    private RenderSupport renderSupport;

    private Element element;

    private String clientId;

    private static class ProcessSubmission implements ComponentAction<SubmitContext> {
        private final String elementName;
        private final String context;

        public ProcessSubmission(String elementName, String context) {
            this.elementName = elementName;
            this.context = context;
        }

        public void execute(SubmitContext component) {
            component.processSubmission(elementName, context);
        }
    }

    public SubmitContext() {
    }

    SubmitContext(Request request) {
        this.request = request;
    }

    void beginRender(MarkupWriter writer) {
        clientId = null;

        String name = formSupport.allocateControlName(resources.getId());

        // Save the element, to see if an id is later requested.

        String type = image == null ? "submit" : "image";

        element = writer.element("input", "type", type, "name", name);

        if (disabled) writer.attributes("disabled", "disabled");

        if (image != null) writer.attributes("src", image.toClientURL());

        formSupport.store(this, new ProcessSubmission(name, context));

        resources.renderInformalParameters(writer);
    }

    void afterRender(MarkupWriter writer) {
        writer.end();
    }

    void processSubmission(final String elementName, final String context)
    {
        if (disabled) return;

        String value = request.getParameter(elementName);

        if (value == null) return;

        Runnable sendNotification = new Runnable()
        {
            public void run()
            {
                resources.triggerEvent(event, new Object[] {context}, null);
            }
        };

        // When not deferred, don't wait, fire the event now (actually, at the end of the current
        // heartbeat). This is most likely because the Submit is inside a Loop and some contextual
        // information will change if we defer.

        if (defer) formSupport.defer(sendNotification);
        else heartbeat.defer(sendNotification);
    }

    // For testing:

    void setDefer(boolean defer) {
        this.defer = defer;
    }

    void setup(ComponentResources resources, FormSupport formSupport, Heartbeat heartbeat, RenderSupport renderSupport) {
        this.resources = resources;
        this.formSupport = formSupport;
        this.heartbeat = heartbeat;
        this.renderSupport = renderSupport;
    }

    /**
     * Returns the component's client id. This must be called after the component has rendered. The id is allocated
     * lazily (first time this method is invoked).
     *
     * @return client id for the component
     */
    public String getClientId()
    {
        if (clientId == null)
        {
            clientId = renderSupport.allocateClientId(resources);

            element.forceAttributes("id", clientId);
        }

        return clientId;
    }
}
  • No labels