001// Copyright 2006-2013 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
015package org.apache.tapestry5.corelib.components;
016
017import org.apache.tapestry5.*;
018import org.apache.tapestry5.annotations.Environmental;
019import org.apache.tapestry5.annotations.HeartbeatDeferred;
020import org.apache.tapestry5.annotations.Parameter;
021import org.apache.tapestry5.annotations.SupportsInformalParameters;
022import org.apache.tapestry5.dom.Element;
023import org.apache.tapestry5.ioc.annotations.Inject;
024import org.apache.tapestry5.ioc.internal.util.InternalUtils;
025
026/**
027 * Generates a <label> element for a particular field. It writes the CSS class "control-label".
028 * <p/>
029 * A Label will render its body, if it has one. However, in most cases it will not have a body, and will render its
030 * {@linkplain org.apache.tapestry5.Field#getLabel() field's label} as its body. Remember, however, that it is the
031 * field label that will be used in any error messages. The Label component allows for client- and server-side
032 * validation error decorations.
033 *
034 * @tapestrydoc
035 */
036@SupportsInformalParameters
037public class Label
038{
039    /**
040     * The for parameter is used to identify the {@link Field} linked to this label (it is named this way because it
041     * results in the for attribute of the label element).
042     */
043    @Parameter(name = "for", required = true, allowNull = false, defaultPrefix = BindingConstants.COMPONENT)
044    private Field field;
045
046    @Environmental
047    private ValidationDecorator decorator;
048
049    @Inject
050    private ComponentResources resources;
051
052    /**
053     * If true, then the body of the label element (in the template) is ignored. This is used when a designer places a
054     * value inside the &lt;label&gt; element for WYSIWYG purposes, but it should be replaced with a different
055     * (probably, localized) value at runtime. The default is false, so a body will be used if present and the field's
056     * label will only be used if the body is empty or blank.
057     */
058    @Parameter
059    private boolean ignoreBody;
060
061    private Element labelElement;
062
063    boolean beginRender(MarkupWriter writer)
064    {
065        decorator.beforeLabel(field);
066
067        labelElement = writer.element("label", "class", "control-label");
068
069        resources.renderInformalParameters(writer);
070
071        // Since we don't know if the field has rendered yet, we need to defer writing the for and id
072        // attributes until we know the field has rendered (and set its clientId property). That's
073        // exactly what Heartbeat is for.
074
075        updateAttributes();
076
077        return !ignoreBody;
078    }
079
080    @HeartbeatDeferred
081    private void updateAttributes()
082    {
083        String fieldId = field.getClientId();
084
085        labelElement.forceAttributes("for", fieldId);
086
087        decorator.insideLabel(field, labelElement);
088    }
089
090    void afterRender(MarkupWriter writer)
091    {
092        // If the Label element has a body that renders some non-blank output, that takes precedence
093        // over the label string provided by the field.
094
095        boolean bodyIsBlank = InternalUtils.isBlank(labelElement.getChildMarkup());
096
097        if (bodyIsBlank)
098            writer.write(field.getLabel());
099
100        writer.end(); // label
101
102        decorator.afterLabel(field);
103    }
104}