    001// Copyright 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.mixins;
    016
    017import org.apache.tapestry5.Field;
    018import org.apache.tapestry5.MarkupWriter;
    019import org.apache.tapestry5.SymbolConstants;
    020import org.apache.tapestry5.ValidationDecorator;
    021import org.apache.tapestry5.annotations.Environmental;
    022import org.apache.tapestry5.annotations.HeartbeatDeferred;
    023import org.apache.tapestry5.annotations.InjectContainer;
    024import org.apache.tapestry5.dom.Element;
    025import org.apache.tapestry5.ioc.annotations.Inject;
    026import org.apache.tapestry5.ioc.annotations.Symbol;
    027
    028/**
    029 * <p>Applied to a {@link org.apache.tapestry5.Field}, this provides the outer layers of markup to correctly
    030 * render text fields, selects, and textareas using Bootstrap:
    031 * an outer {@code <div class="field-group">} containing a {@code <label class="control-label">} and the field itself.
    032 * Actually, the class attribute of the div is defined by the
    033 * {@link SymbolConstants#FORM_GROUP_WRAPPER_CSS_CLASS} and
    034 * the class attribute of label is defined by the {@link SymbolConstants#FORM_GROUP_LABEL_CSS_CLASS}.
    035 * <code>field-group</code> and <code>control-label</code> are the default values.
    036 * As with the {@link org.apache.tapestry5.corelib.components.Label} component, the {@code for} attribute is set (after the field itself
    037 * renders).
    038 * </p>
    039 * <p>
    040 * You can also use the {@link SymbolConstants#FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_NAME} symbol
    041 * to optionally wrap the input field in an element and {@link SymbolConstants#FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_CSS_CLASS}
    042 * to give it a CSS class. This is useful for Bootstrap form-horizontal forms.
    043 * Setting {@link SymbolConstants#FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_NAME} to <code>div</code>,
    044 * {@link SymbolConstants#FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_CSS_CLASS} to <code>col-sm-10</code>
    045 * and {@link SymbolConstants#FORM_GROUP_LABEL_CSS_CLASS} to <code>col-sm-2</code>
    046 * will generate labels 2 columns wide and form fields 10 columns wide.
    047 * </p>
    048 * <p>
    049 * This component is not appropriate for radio buttons or checkboxes as they use a different class on the outermost element
    050 * ("radio" or "checkbox") and next the element inside the {@code <label>}.
    051 * </p>
    052 *
    053 * @tapestrydoc
    054 * @since 5.4
    055 * @see SymbolConstants#FORM_GROUP_WRAPPER_CSS_CLASS
    056 * @see SymbolConstants#FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_NAME
    057 * @see SymbolConstants#FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_CSS_CLASS
    058 * @see SymbolConstants#FORM_GROUP_LABEL_CSS_CLASS
    059 * @see SymbolConstants#FORM_FIELD_CSS_CLASS
    060 */
    061public class FormGroup
    062{
    063    @InjectContainer
    064    private Field field;
    065
    066    @Inject
    067    @Symbol(SymbolConstants.FORM_GROUP_LABEL_CSS_CLASS)
    068    private String labelCssClass;
    069
    070    @Inject
    071    @Symbol(SymbolConstants.FORM_GROUP_WRAPPER_CSS_CLASS)
    072    private String divCssClass;
    073
    074    @Inject
    075    @Symbol(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_NAME)
    076    private String fieldWrapperElementName;
    077
    078    @Inject
    079    @Symbol(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_CSS_CLASS)
    080    private String fieldWrapperElementCssClass;
    081
    082    private Element label;
    083
    084    private Element fieldWrapper;
    085
    086    @Environmental
    087    private ValidationDecorator decorator;
    088
    089    void beginRender(MarkupWriter writer)
    090    {
    091        writer.element("div", "class", divCssClass);
    092
    093        decorator.beforeLabel(field);
    094
    095        label = writer.element("label", "class", labelCssClass);
    096        writer.end();
    097
    098        fillInLabelAttributes();
    099
    100        decorator.afterLabel(field);
    101
    102        if (fieldWrapperElementName.length() > 0) {
    103            fieldWrapper = writer.element(fieldWrapperElementName);
    104            if (fieldWrapperElementCssClass.length() > 0) {
    105                fieldWrapper.attribute("class", fieldWrapperElementCssClass);
    106            }
    107        }
    108
    109    }
    110
    111    @HeartbeatDeferred
    112    void fillInLabelAttributes()
    113    {
    114        label.attribute("for", field.getClientId());
    115        label.text(field.getLabel());
    116    }
    117
    118    void afterRender(MarkupWriter writer)
    119    {
    120        if (fieldWrapper != null) {
    121            writer.end(); // field wrapper
    122        }
    123        writer.end(); // div.form-group
    124    }
    125}