问题
I have a Parent class that is instantiated twice in ParentForm backing class as mother and father. The web form expects a first and last name to be validated for each Parent as NotNull. I created a Thymeleaf fragment specifically for the "mother" instance, but I want to make the fragment generic for both Parent instances:
<div class="form-group" th:fragment="name">
<div class="form-row clearfix">
<div th:classappend="${#fields.hasErrors('mother.firstName') or #fields.hasErrors('mother.lastName')} ? has-error : ''">
<label class="control-label col-xs-12" th:classappend="${#fields.hasErrors('mother.firstName') or #fields.hasErrors('mother.lastName')} ? has-error : ''">What is the mother's name?</label>
</div>
<div class="col-sm-11 col-sm-offset-1 col-xs-12">
<div class="form-row form-group">
<div th:classappend="${#fields.hasErrors('mother.firstName')} ? has-error : ''" class="col-xs-12 col-sm-6">
<div class="input-group">
<label th:for="mother.firstName" class="control-label">First </label>
<input type="text" th:field="*{mother.firstName}" th:value="*{mother.firstName}" class="form-control"/>
</div>
</div>
<div th:classappend="${#fields.hasErrors('mother.lastName')} ? has-error : ''" class="col-xs-12 col-sm-6">
<div class="input-group">
<label th:for="mother.lastName" class="control-label">Last </label>
<input type="text" th:field="*{mother.lastName}" th:value="*{mother.lastName}" class="form-control"/>
</div>
</div>
</div>
</div>
</div>
<div th:if="${#fields.hasErrors('mother.firstName') or #fields.hasErrors('mother.lastName')}" class="form-row clearfix has-error">
<div class="help-block small">
<ul>
<li th:each="err : ${#fields.errors('mother.firstName')}" th:text="${err}"></li>
<li th:each="err : ${#fields.errors('mother.lastName')}" th:text="${err}"></li>
</ul>
</div>
</div>
</div>
The Parent class has firstName and lastName along with the getters/setters using standard naming convention. This all works as expected - validation, display, binding.
So I'm trying to make the fragment generic by changing the signature to something like th:fragment="name(parent, parentType)" and replacing the "mother" instances to "parent" instances like so:
<div class="form-group" th:fragment="name(parent, parentType)">
<div class="form-row clearfix">
<div th:classappend="${#fields.hasErrors('parent.firstName') or #fields.hasErrors('parent.lastName')} ? has-error : ''">
<label class="control-label col-xs-12" th:classappend="${#fields.hasErrors('parent.firstName') or #fields.hasErrors('parent.lastName')} ? has-error : ''" th:text="|What is the ${parentType}'s name?|"></label>
</div>
<div class="col-sm-11 col-sm-offset-1 col-xs-12">
<div class="form-row form-group">
<div th:classappend="${#fields.hasErrors('parent.firstName')} ? has-error : ''" class="col-xs-12 col-sm-6">
<div class="input-group">
<label th:for="parent.firstName" class="control-label">First </label>
<input type="text" th:field="*{parent.firstName}" th:value="*{parent.firstName}" class="form-control"/>
</div>
</div>
<div th:classappend="${#fields.hasErrors('parent.lastName')} ? has-error : ''" class="col-xs-12 col-sm-6">
<div class="input-group">
<label th:for="parent.lastName" class="control-label">Last </label>
<input type="text" th:field="*{parent.lastName}" th:value="*{parent.lastName}" class="form-control"/>
</div>
</div>
</div>
</div>
</div>
<div th:if="${#fields.hasErrors('parent.firstName') or #fields.hasErrors('parent.lastName')}" class="form-row clearfix has-error">
<div class="help-block small">
<ul>
<li th:each="err : ${#fields.errors('parent.firstName')}" th:text="${err}"></li>
<li th:each="err : ${#fields.errors('parent.lastName')}" th:text="${err}"></li>
</ul>
</div>
</div>
</div>
Where "parent" is the instance of the object and "parentType" is simply the string value for the parent type (e.g. "mother" or "father") for display purposes.
<div th:replace="fragments/survey/mother::name(*{mother}, 'mother')"></div>
The errors that I get when I try it this way:
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "#fields.hasErrors('parent.firstName') or #fields.hasErrors('parent.lastName')" (template: "fragments/survey/mother" - line 7, col 18)
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#fields.hasErrors('parent.firstName') or #fields.hasErrors('parent.lastName')" (template: "fragments/survey/mother" - line 7, col 18)
How can I reference the field in the #fields.hasErrors() method in a generic way?
Caused by: org.springframework.beans.NotReadablePropertyException: Invalid property 'parent' of bean class [g.s.m.ParentForm]: Bean property 'parent' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Obviously, the bean properties are named "mother" and "father," so how do I get this generic fragment to bind to "mother" and "father" rather than "parent?"
回答1:
So, first you have to realize that for stuff like th:field
and #fields.hasErrors()
, the string result has to match the complete path to the backing object (and not to a local variable like parent
). You cannot use *{parent.firstName}
because ParentForm has no getParent().getFirstName(). In order to do this, you have to use preprocessing
Instead of passing the object to the fragment, all you need is the object name. (In your case, since parentType already has mother or father, I'm going to use them in the examples.) After making these changes, your fields should look something like this:
<div th:classappend="${#fields.hasErrors(parentType + '.firstName')} ? has-error : ''" class="col-xs-12 col-sm-6">
<div class="input-group">
<label th:for="${parentType + '.firstName'}" class="control-label">First </label>
<input type="text" th:field="*{__${parentType}__.firstName}" class="form-control"/>
</div>
</div>
<div th:classappend="${#fields.hasErrors(parentType + '.lastName')} ? has-error : ''" class="col-xs-12 col-sm-6">
<div class="input-group">
<label th:for="${parentType + '.lastName'}" class="control-label">Last </label>
<input type="text" th:field="*{__${parentType}__.firstName}" class="form-control"/>
</div>
</div>
Also, a sidenote... if you are using th:field
, you do not have to use th:value
. th:field
does that for you.
来源:https://stackoverflow.com/questions/43353628/reference-object-passed-to-thymeleaf-fragment