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 it's 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 <label> 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}