/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.tags.form;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTag;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
The <errors>
tag renders field errors in an HTML 'span' tag. Displays errors for either an object or a particular field. This tag supports three main usage patterns:
- Field only - set '
path
' to the field name (or path)
- Object errors only - omit '
path
'
- All errors - set '
path
' to '*
'
Attribute Summary
Attribute
Required?
Runtime Expression?
Description
cssClass
false
true
HTML Optional Attribute
cssStyle
false
true
HTML Optional Attribute
delimiter
false
true
Delimiter for displaying multiple error messages.
Defaults to the br tag.
dir
false
true
HTML Standard Attribute
element
false
true
Specifies the HTML element that is used to render the enclosing
errors.
htmlEscape
false
true
Enable/disable HTML escaping of rendered values.
id
false
true
HTML Standard Attribute
lang
false
true
HTML Standard Attribute
onclick
false
true
HTML Event Attribute
ondblclick
false
true
HTML Event Attribute
onkeydown
false
true
HTML Event Attribute
onkeypress
false
true
HTML Event Attribute
onkeyup
false
true
HTML Event Attribute
onmousedown
false
true
HTML Event Attribute
onmousemove
false
true
HTML Event Attribute
onmouseout
false
true
HTML Event Attribute
onmouseover
false
true
HTML Event Attribute
onmouseup
false
true
HTML Event Attribute
path
false
true
Path to errors object for data binding
tabindex
false
true
HTML Standard Attribute
title
false
true
HTML Standard Attribute
Author: Rob Harrop, Juergen Hoeller, Rick Evans Since: 2.0
/**
* The {@code <errors>} tag renders field errors in an HTML 'span' tag.
* Displays errors for either an object or a particular field.
*
* <p>This tag supports three main usage patterns:
*
* <ol>
* <li>Field only - set '{@code path}' to the field name (or path)</li>
* <li>Object errors only - omit '{@code path}'</li>
* <li>All errors - set '{@code path}' to '{@code *}'</li>
* </ol>
*
* <p>
* <table>
* <caption>Attribute Summary</caption>
* <thead>
* <tr>
* <th class="colFirst">Attribute</th>
* <th class="colOne">Required?</th>
* <th class="colOne">Runtime Expression?</th>
* <th class="colLast">Description</th>
* </tr>
* </thead>
* <tbody>
* <tr class="altColor">
* <td><p>cssClass</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Optional Attribute</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>cssStyle</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Optional Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>delimiter</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>Delimiter for displaying multiple error messages.
* Defaults to the br tag.</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>dir</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Standard Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>element</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>Specifies the HTML element that is used to render the enclosing
* errors.</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>htmlEscape</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>Enable/disable HTML escaping of rendered values.</p></td>
* </tr>
* <tr class="altColor">
* <td><p>id</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Standard Attribute</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>lang</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Standard Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>onclick</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>ondblclick</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>onkeydown</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>onkeypress</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>onkeyup</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>onmousedown</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>onmousemove</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>onmouseout</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>onmouseover</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>onmouseup</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Event Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>path</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>Path to errors object for data binding</p></td>
* </tr>
* <tr class="rowColor">
* <td><p>tabindex</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Standard Attribute</p></td>
* </tr>
* <tr class="altColor">
* <td><p>title</p></td>
* <td><p>false</p></td>
* <td><p>true</p></td>
* <td><p>HTML Standard Attribute</p></td>
* </tr>
* </tbody>
* </table>
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Rick Evans
* @since 2.0
*/
@SuppressWarnings("serial")
public class ErrorsTag extends AbstractHtmlElementBodyTag implements BodyTag {
The key under which this tag exposes error messages in the page context scope
. /**
* The key under which this tag exposes error messages in
* the {@link PageContext#PAGE_SCOPE page context scope}.
*/
public static final String MESSAGES_ATTRIBUTE = "messages";
The HTML 'span
' tag. /**
* The HTML '{@code span}' tag.
*/
public static final String SPAN_TAG = "span";
private String element = SPAN_TAG;
private String delimiter = "<br/>";
Stores any value that existed in the 'errors messages' before the tag was started.
/**
* Stores any value that existed in the 'errors messages' before the tag was started.
*/
@Nullable
private Object oldMessages;
private boolean errorMessagesWereExposed;
Set the HTML element must be used to render the error messages.
Defaults to an HTML '<span/>
' tag.
/**
* Set the HTML element must be used to render the error messages.
* <p>Defaults to an HTML '{@code <span/>}' tag.
*/
public void setElement(String element) {
Assert.hasText(element, "'element' cannot be null or blank");
this.element = element;
}
Get the HTML element must be used to render the error messages.
/**
* Get the HTML element must be used to render the error messages.
*/
public String getElement() {
return this.element;
}
Set the delimiter to be used between error messages.
Defaults to an HTML '<br/>
' tag.
/**
* Set the delimiter to be used between error messages.
* <p>Defaults to an HTML '{@code <br/>}' tag.
*/
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
Return the delimiter to be used between error messages.
/**
* Return the delimiter to be used between error messages.
*/
public String getDelimiter() {
return this.delimiter;
}
Get the value for the HTML 'id
' attribute. Appends '.errors
' to the value returned by getPropertyPath()
or to the model attribute name if the <form:errors/>
tag's 'path
' attribute has been omitted.
See Also: - getPropertyPath()
Returns: the value for the HTML 'id
' attribute
/**
* Get the value for the HTML '{@code id}' attribute.
* <p>Appends '{@code .errors}' to the value returned by {@link #getPropertyPath()}
* or to the model attribute name if the {@code <form:errors/>} tag's
* '{@code path}' attribute has been omitted.
* @return the value for the HTML '{@code id}' attribute
* @see #getPropertyPath()
*/
@Override
protected String autogenerateId() throws JspException {
String path = getPropertyPath();
if (!StringUtils.hasLength(path) || "*".equals(path)) {
path = (String) this.pageContext.getAttribute(
FormTag.MODEL_ATTRIBUTE_VARIABLE_NAME, PageContext.REQUEST_SCOPE);
}
return StringUtils.deleteAny(path, "[]") + ".errors";
}
Get the value for the HTML 'name
' attribute. Simply returns null
because the 'name
' attribute is not a validate attribute for the 'span
' element.
/**
* Get the value for the HTML '{@code name}' attribute.
* <p>Simply returns {@code null} because the '{@code name}' attribute
* is not a validate attribute for the '{@code span}' element.
*/
@Override
@Nullable
protected String getName() throws JspException {
return null;
}
Should rendering of this tag proceed at all?
Only renders output when there are errors for the configured path
.
Returns: true
only when there are errors for the configured path
/**
* Should rendering of this tag proceed at all?
* <p>Only renders output when there are errors for the configured {@link #setPath path}.
* @return {@code true} only when there are errors for the configured {@link #setPath path}
*/
@Override
protected boolean shouldRender() throws JspException {
try {
return getBindStatus().isError();
}
catch (IllegalStateException ex) {
// Neither BindingResult nor target object available.
return false;
}
}
@Override
protected void renderDefaultContent(TagWriter tagWriter) throws JspException {
tagWriter.startTag(getElement());
writeDefaultAttributes(tagWriter);
String delimiter = ObjectUtils.getDisplayString(evaluate("delimiter", getDelimiter()));
String[] errorMessages = getBindStatus().getErrorMessages();
for (int i = 0; i < errorMessages.length; i++) {
String errorMessage = errorMessages[i];
if (i > 0) {
tagWriter.appendValue(delimiter);
}
tagWriter.appendValue(getDisplayString(errorMessage));
}
tagWriter.endTag();
}
Exposes any bind status error messages under this key
in the PAGE_SCOPE.PAGE_SCOPE
. Only called if shouldRender()
returns true
.
See Also:
/**
* Exposes any bind status error messages under {@link #MESSAGES_ATTRIBUTE this key}
* in the {@link PageContext#PAGE_SCOPE}.
* <p>Only called if {@link #shouldRender()} returns {@code true}.
* @see #removeAttributes()
*/
@Override
protected void exposeAttributes() throws JspException {
List<String> errorMessages = new ArrayList<>(Arrays.asList(getBindStatus().getErrorMessages()));
this.oldMessages = this.pageContext.getAttribute(MESSAGES_ATTRIBUTE, PageContext.PAGE_SCOPE);
this.pageContext.setAttribute(MESSAGES_ATTRIBUTE, errorMessages, PageContext.PAGE_SCOPE);
this.errorMessagesWereExposed = true;
}
Removes any bind status error messages that were previously stored under this key
in the PAGE_SCOPE.PAGE_SCOPE
. See Also: - exposeAttributes()
/**
* Removes any bind status error messages that were previously stored under
* {@link #MESSAGES_ATTRIBUTE this key} in the {@link PageContext#PAGE_SCOPE}.
* @see #exposeAttributes()
*/
@Override
protected void removeAttributes() {
if (this.errorMessagesWereExposed) {
if (this.oldMessages != null) {
this.pageContext.setAttribute(MESSAGES_ATTRIBUTE, this.oldMessages, PageContext.PAGE_SCOPE);
this.oldMessages = null;
}
else {
this.pageContext.removeAttribute(MESSAGES_ATTRIBUTE, PageContext.PAGE_SCOPE);
}
}
}
}