    001    // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.corelib.components;
    016
    017    import org.apache.tapestry5.BindingConstants;
    018    import org.apache.tapestry5.ComponentResources;
    019    import org.apache.tapestry5.Field;
    020    import org.apache.tapestry5.MarkupWriter;
    021    import org.apache.tapestry5.ValidationDecorator;
    022    import org.apache.tapestry5.annotations.Environmental;
    023    import org.apache.tapestry5.annotations.HeartbeatDeferred;
    024    import org.apache.tapestry5.annotations.Parameter;
    025    import org.apache.tapestry5.annotations.SupportsInformalParameters;
    026    import org.apache.tapestry5.dom.Element;
    027    import org.apache.tapestry5.ioc.annotations.Inject;
    028    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    029
    030    /**
    031     * Generates a &lt;label&gt; element for a particular field.
    032     * <p/>
    033     * A Label will render its body, if it has one. However, in most cases it will not have a body, and will render its
    034     * {@linkplain org.apache.tapestry5.Field#getLabel() field's label} as it's body. Remember, however, that it is the
    035     * field label that will be used in any error messages. The Label component allows for client- and server-side
    036     * validation error decorations.
    037     *
    038     * @tapestrydoc
    039     */
    040    @SupportsInformalParameters
    041    public class Label
    042    {
    043        /**
    044         * The for parameter is used to identify the {@link Field} linked to this label (it is named this way because it
    045         * results in the for attribute of the label element).
    046         */
    047        @Parameter(name = "for", required = true, allowNull = false, defaultPrefix = BindingConstants.COMPONENT)
    048        private Field field;
    049
    050        @Environmental
    051        private ValidationDecorator decorator;
    052
    053        @Inject
    054        private ComponentResources resources;
    055
    056        /**
    057         * If true, then the body of the label element (in the template) is ignored. This is used when a designer places a
    058         * value inside the &lt;label&gt; element for WYSIWYG purposes, but it should be replaced with a different
    059         * (probably, localized) value at runtime. The default is false, so a body will be used if present and the field's
    060         * label will only be used if the body is empty or blank.
    061         */
    062        @Parameter
    063        private boolean ignoreBody;
    064
    065        private Element labelElement;
    066
    067        boolean beginRender(MarkupWriter writer)
    068        {
    069            decorator.beforeLabel(field);
    070
    071            labelElement = writer.element("label");
    072
    073            resources.renderInformalParameters(writer);
    074
    075            // Since we don't know if the field has rendered yet, we need to defer writing the for and id
    076            // attributes until we know the field has rendered (and set its clientId property). That's
    077            // exactly what Heartbeat is for.
    078
    079            updateAttributes();
    080
    081            return !ignoreBody;
    082        }
    083
    084        @HeartbeatDeferred
    085        private void updateAttributes()
    086        {
    087            String fieldId = field.getClientId();
    088
    089            labelElement.forceAttributes("for", fieldId);
    090
    091            decorator.insideLabel(field, labelElement);
    092        }
    093
    094        void afterRender(MarkupWriter writer)
    095        {
    096            // If the Label element has a body that renders some non-blank output, that takes precedence
    097            // over the label string provided by the field.
    098
    099            boolean bodyIsBlank = InternalUtils.isBlank(labelElement.getChildMarkup());
    100
    101            if (bodyIsBlank)
    102                writer.write(field.getLabel());
    103
    104            writer.end(); // label
    105
    106            decorator.afterLabel(field);
    107        }
    108    }