I would like to populate a radio input control in a Thymeleaf automatically from a Java enum type called "source". I am using Spring Boot in the backend.
My controller initialises a list of the enum values like this:
this.sourcesAsList = Arrays.asList(Source.values());
model.addAttribute("sources", sourcesAsList);
This works fine, at least as far as the List is concerned (as I can see in the log output).
The Thymeleaf template then tries to instantiate a radio control based on this attribute in the model, like so:
<div th:each="source : ${sources}">
<input type="radio" th:field="*{source}" th:value="${source.value}"/><label th:text="| ${source.value} |">Should not see this !</label>
</div>
However, when I try to load this page, I get the following error:
[Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/feedbackapp2.html]")] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'source' available as request attribute
The enum is pretty simple:
public enum Source {
VALONE, VALTWO, VALTHREE, VALFOUR;
public String getName() {
return this.name();
}
}
It's the first time I work with Thymeleaf, so I guess it is just a simple syntax problem somewhere, but even after googling I couldn't find a working example. Any ideas? Is it possible at all to do this with an enum? If not, what kind of datatype would be more appropriate?
Thanks a lot.
Cheers,
Martin
I played around a bit more and got it working. The following HTML snippet displays the radio buttons correctly based the list of Enums and also is wired to the model correctly, in that I received the selected value in the controller's POST method:
<div th:each="source : ${sources}">
<input name="source" type="radio" th:value="${source}"/><label for="source" th:text="| ${source} |">Something is wrong !</label>
</div>
There were two problems:
it is not necessary to access the name() attribute of the enum (so,
using ${source} instead of ${source.name} is fine)
instead of th:field, use the namne attribute of the input control
Thanks a lot to Periklis for the comment.
Related
I have encountered the following code in a Spring-based Java application.
This is the controller:
#RequestMapping(value="/plants/form")
public String form(Model model){
model.addAttribute("plant", new Plant());
return "plants/create";
}
I have several questions regarding this snippet.
I know that form method is called for requests to /plants/form,
but I would like to know which method invokes form and passes arguments to it?
To me, it seems that a model is like a database. So, it looks like
model.addAttribute("plant", new Plant()); creates a Plant
instance and makes it accessible in a model under the name plant.
Thus, an attribute of a model seems to be
something like a field in DB. What is the exact meaning of
an attribute of a model?
What is value in #RequestMapping(value="/plants/form") and what
is the difference between aforementioned notation and
#RequestMapping("/plants/form")?
Consider this incomplete snippet of HTML:
<form class="form-horizontal" role="form" th:object="${plant}" th:method="post" th:action="#{/plants}">
<div class="form-group">
<label for="name" class="col-sm-2 control-label">Name</label>
<input type="text" th:field="*{name}"></input>
</div>
</form>
What is the meaning of $, *, # in "${plant}","*{name}" and
"#{/plants}". It is not clear to me.
1) If I understand your question correctly, then Spring MVC DispatcherServlet is responsible for routing request to your handler method based on configured HandlerMapping/HandlerAdapter.
2) Model is M in MVC, it has nothing in common with relational database. Rather consider it as a Map of String keys to Object values. Spring MVC model attributes are stored in request scope under the hood.
3) There is no difference, value is the attribute of #RequestMapping annotation. When you want to pass a single value argument and no other arguments, then it is possible to omit value by convention.
4) These are tags and attributes of Thymeleaf Standard and SpringStandard dialects. More information here.
I have Spring, Thymeleaf, HTML5 web page. And have problem iterating through Map if map object contains non-english characters.
All the character encoding filters/resolvers/converters are set and forced to UTF-8
Let's say I have the following setup:
Controller:
#RequestMapping(value = "/app", method = { RequestMethod.GET, RequestMethod.POST })
public String view(ModelMap model, #RequestParam(value = "foo", required = false) Integer foo) {
MapDTO mapDto = new MapDTO();
Map<String, List<Foo>> mapFoo ... // populate map etc.
model.add("mapDto", mapDto.setMapFoo(mapFoo))
return foo == null ? VIEW : VIEW + " :: fooFragment"
}
And template for testing purposes:
.. page ..
<div th:fragment="fooFragment" id="fooFragment">
..
<th:block th:each="fooMap : ${mapDto.mapFoo}">
<th:block th:each="item,row : ${fooMap.value.fooList}">
<p th:text="${item.val}"</p> <!-- working OK -->
<p th:text=" ${fooMap.value.fooList[__${row.index}__].idrValueName} "></p> <!-- working OK -->
<input th:field="*{mapDto.mapFoo[__${fooMap.key}__].fooList[__${row.index}__].val} "></p><!-- And this is working -->
<p th:text="${mapDto.mapFoo[__${fooMap.key}__].fooList[__${row.index}__].val} "></p><!-- FAILING ON THIS -->
</th:block>
</th:block>
..
</div>
.. rest of the page ..
Let's say, that i have map with keys "A" and "Ā".
I'm getting following exception:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "mapDto.mapFoo[Ā].fooList[0].val"
..
Caused by:
java.lang.IllegalStateException: Cannot handle (257) 'Ā'
Whats interesting - I have almost exactly the same setup on different page (A bit different mapDto structure and template, but the same principle, same page headers) and I don't have the same issues there.
Maps that don't have keys with non-english characters behaves as predicted.
Any ideas where lies the problem?
SOLVED:
Solved described issue using double-bracket syntax
${{...}}
There are some limitations with it. But I was able to work around them it solved my main issue.
mapDto.mapFoo[Ā].fooList[0].val
This expression contains accented character Ā. Looking at the source code of Tokenizer, accented characters are clearly indicated as an invalid candidate for tokenization.
Spring EL's InternalSpelExpressionParser which is responsible for parsing expressions is tightly coupled to this Tokenizer, so I don't think there is an out of the box way to allow tokenization of accented characters. I'm pretty sure there is a good reason why this is not allowed (e.g. it might break other parts of the framework) so I suggest you go along to what the framework is demanding and remove the accented characters in the expression.
How can I call a controller method from within a Play! template?
I have a default controller, Application, and the hasliked() method inside that controller.
The method returns whether the logged in user has liked the post ID.
It returns "none" if the user has liked the post, otherwise it returns "block" (for the CSS)
I have added the following route: GET /hasliked/{id} Application.hasliked
I tried the following:
#{list items:postList, as:'post'}
%{
display = Application.hasliked(post.id);
%}
<div style="display: ${display}">...</div>
#{/list}
But I get this error:
Template execution error
Execution error occured in template /app/views/Application/dashboard.html. Exception raised was NullPointerException : Cannot invoke method hasliked() on null object.
Try using a fully qualified name like:
controllers.Application.hasliked()
EDIT on comment:
The issue with your exception is that you are accessing the controller to get a value. That's wrong.
Controllers in Play are used to navigate. They are static, they return "void", and they do a call to another controller method or to a render method. What you try to do may have unexpected results.
What you want to do is to get the value inside the controller and pass it as a parameter:
//On controller
public static void yourRequest() {
//...
Object display = getDisplay(); //get your value
render(display);
}
//On template
<div style="display: ${display}">...</div>
That's the recommended way.
The exception you get is (most likely) caused because your Application.hasLiked() ends up with a redirect call (either render() or call to another controller's method) and that's happening while you render the page corresponding to the initial call. So it breaks.
It would probably be a better way to do fill the information that is required into the list of items instead of calling back the controller:
Your template doesn't need to know about your controllers. It should just convert data to HTML and not acquire data from somewhere else. That's the task of the controller.
It would also be more efficient in terms of database access to fetch the like status for all items at once instead of doing several calls.
When doing refactoring (e.g. renaming methods etc of your controller) the IDE cannot help you if you call controllers from the template (unless it's aware of how Play! templates work).
If you really must do this (and again, you shouldn't) you need to fully qualify the name of the controller:
controllers.Application.hasLiked()
just like Pere Villega pointed out.
An alternative to this may be to issue an AJAX call to set the style, rather than using the controller.
Set the style of your "liked" element to display: none by default, and when your view has rendered, issue an GET request to /hasliked/ with the ID as a parameter and update your CSS styles accordingly: when the user has not already liked this, output false (or whatever you want), so that you can use JavaScript to re-define the style.
The easiest way to do this would be to use jQuery to issue a request to your controller when the view has loaded. Have a look at the Play! documentation on AJAX for some inspiration. Note that you don't have to use #{jsAction /} at all - personally I find it easier to define the jQuery calls myself.
Try the following:
<script type="text/javascript" language="javascript">
var url = "#{Application.hasliked}" + "/" + yourId;
window.location.replace(url);
</script>
This question already has answers here:
How to populate options of h:selectOneMenu from database?
(5 answers)
Closed 6 years ago.
Up until now, I have always been using JSP to display pages. When a user request for a page such as "Add Item", I will load all Item Category in an Array List and display them as options in select box like this:
<select name="category>
<%
ArrayList<Category> categories = (ArrayList<Category>) request.getAttribute("categories");
for (Category c : data) {
%>
<option value="<%= c.getId() %>"><%= c.getName() %></option>
<%
}
%>
</select>
From the book "JavaServer Faces 2.0, The Complete Reference", I learnt that: "JSF enforces clean Model-View-Controller separation by disallowing the inclusion of Java code in markup pages". Hence, I'd be very grateful if someone could show me how I can handle the above task using JSF since I cannot use Java code as I have always done anymore.
Best regards,
James Tran
JSF 2.0 uses Facelets as the templating method, which in a nutshell is XHTML with some additional elements.
While technically you can perform method calls from Facelets, in general the idea is to access a JavaBean with proper geter/setter methods to perform your data moving. You can accomplish this as the below segment of code shows:
<h:selectOneMenu value="#{backingBean.selectedCategory}">
<f:selectItems value="#{backingBean.categoryList}"/>
</h:selectOneMenu>
On the bean side of things, you want to expose a bean to JSF using either faces-config (which is largely discouraged) or a mechanism such as CDI or the Managed Bean infrastructure. I highly recommend you look into using SEAM if you go the CDI route, as it will unify the (currently really strangely disparate) Managed Bean and CDI frameworks, so you can use JSF scopes in CDI, and have CDI beans available in JSF scopes.
#ManagedBean(name="backingBean")
#ViewScoped
public class MyJavaBackingBean {
#ManagedProperty("#{param.categories}")
protected List<String> categoryList
public void setSelectedCategory(String value) {
this.selectedCategory = value;
}
public String getSelectedCategory() {
return this.property;
}
...
}
You can also make the getters do lazy initialization of your values (for pulling categoryList from a database for example), and use some other JSF annotations to do various initialization tasks.
You can also code action methods which return a String representing the JSF action (this gets coded into your faces-context.xml file) to take after returning. Phase listeners on the backing bean can also be called at various stages of page rendering, validation and submission, getting you very fine grained control.
categoryList in the above example is not limited to basic types of course, and <f:selectItems> also has some syntax for writing out the textual version of your select items, so you can make some quite complex expressions to display each item in a friendly way.
Create a bean and make it known with e.g. #Named so you can refer to it from your JSF script. Then give that bean a method returning the data you want to show, and invoke that method from your JSF script in a location where that data is expected e.g. a loop construct.
Store the data you want to display in a Java list, and expose that list as a property of a backing bean. The use the appropriate JSF tag to display that property.
In JSF 2.0 you can include the tag h:selectOneMenu in which you get the value where you store the item value selected. The value in f:selectItems could be a collection of any object the most of times SelectItem in this object your declare value object and the label to display.
<h:selectOneMenu value="#{backingBean.selectedvalue}">
<f:selectItems value="#{backingBean.List}"/> </h:selectOneMenu>
if you required values and labels of another object in you must declare
<h:selectOneMenu value="#{backingBean.selectedvalue}">
<f:selectItems value="#{backingBean.ListCar}" var="car" itemLabel="#{car.model}" itemValue="#{car.modelId}"/>
</h:selectOneMenu>
I'm building a spring mvc application.
Now the problem I have is the following.
I have a controller which adds a DayInfo[][] to my ModelMap.
(DayInfo has an id, a title (String) and Text(also String).
Now the problem I have is that I have no problem displaying this DayInfo[][] with <foreach> tags in my jsp.
However I'm outputting the Title property as an input box(type text), and I'd like to be able to update this value (and thus saving it to be a database but that shouldn't be a problem). However I'm having trouble with binding this value to the input box so that it is actually returned to the controller.
If anyone has some advice it would be welcome.
I have never done this with multidimensional arrays but it should be something like this (though I haven't tried it, it's just to give you an idea). In the JSP you should set the name of the input with each index, something like this:
<c:forEach var="row" items="${days}" varStatus="statusRow">
<c:forEach var="day" items="${row}" varStatus="statusCol">
<input type="text" name="days[${statusRow.index}][${statusCol.index}].title" value="${day.title}"/>
</c:forEach>
</c:forEach>
and in the controller you have to prepare days variable so the size of the array is the same as the one you get from the JSP. So you can use #ModelAttribute method to prepare the array (this method will be executed before the #RequestMapping method).
#ModelAttribute("days")
public getDays(){
DayInfo[][] days;
//Here you have to instantiate the days to prepare it so it can be filled
//You can load for example the data from the database
return days;
}
#RequestMapping("/yourURL")
public String getFormData(#ModelAttribute("days")DayInfo[][] days){
//Here in days you should have the data from the form overriding
// the one from the database
}
Hope this helps and sorry if there is any error, though I'm writing withoug trying it.