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}