Spring Boot and FreeMarker - spring

I'm trying to work through a simple example of Spring Boot and FreeMarker integration (based on tutorials I've found on the web). For some reason my view is not being resolved to the FreeMarker template (I think that's the issue).
The result when launched in a browser is simply to return the name of the TFL view file i.e. "index". So the controller is being called and returning the string "index", but there seems to be no trigger to pull in the FTL file itself. Any help would be appreciated...
I have the following configuration class where I define the view resolver and the Free Maker config.
#Configuration
public class MvcConfigurer extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache(true);
resolver.setPrefix("");
resolver.setSuffix(".ftl");
resolver.setContentType("text/html; charset=UTF-8");
return resolver;
}
#Bean
public FreeMarkerConfigurer freemarkerConfig() throws IOException, TemplateException {
FreeMarkerConfigurationFactory factory = new FreeMarkerConfigurationFactory();
factory.setTemplateLoaderPaths("classpath:templates", "src/main/resource/templates");
factory.setDefaultEncoding("UTF-8");
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setConfiguration(factory.createConfiguration());
return result;
}
}
Then I have the following controller:
#RestController
public class HelloController {
/**
* Static list of users to simulate Database
*/
private static List<User> userList = new ArrayList<User>();
//Initialize the list with some data for index screen
static {
userList.add(new User("Bill", "Gates"));
userList.add(new User("Steve", "Jobs"));
userList.add(new User("Larry", "Page"));
userList.add(new User("Sergey", "Brin"));
userList.add(new User("Larry", "Ellison"));
}
/**
* Saves the static list of users in model and renders it
* via freemarker template.
*
* #param model
* #return The index view (FTL)
*/
#RequestMapping(value = "/index", method = RequestMethod.GET)
public String index(#ModelAttribute("model") ModelMap model) {
model.addAttribute("userList", userList);
return "index";
}
/**
* Add a new user into static user lists and display the
* same into FTL via redirect
*
* #param user
* #return Redirect to /index page to display user list
*/
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String add(#ModelAttribute("user") User user) {
if (null != user && null != user.getFirstname()
&& null != user.getLastname() && !user.getFirstname().isEmpty()
&& !user.getLastname().isEmpty()) {
synchronized (userList) {
userList.add(user);
}
}
return "redirect:index.html";
}
}
Then finally I have the following FTL file stored in "src/main/resource/templates"
<html>
<head><title>ViralPatel.net - FreeMarker Spring MVC Hello World</title>
<body>
<div id="header">
<H2>
<img height="37" width="236" border="0px" src="http://viralpatel.net/blogs/wp-content/themes/vp/images/logo.png" align="left"/>
FreeMarker Spring MVC Hello World
</H2>
</div>
<div id="content">
<fieldset>
<legend>Add User</legend>
<form name="user" action="add.html" method="post">
Firstname: <input type="text" name="firstname" /> <br/>
Lastname: <input type="text" name="lastname" /> <br/>
<input type="submit" value=" Save " />
</form>
</fieldset>
<br/>
<table class="datatable">
<tr>
<th>Firstname</th> <th>Lastname</th>
</tr>
<#list model["userList"] as user>
<tr>
<td>${user.firstname}</td> <td>${user.lastname}</td>
</tr>
</#list>
</table>
</div>
</body>
</html>

The problem is that your controller has the wrong annotation.
You should use #Controller instead of #RestController
#RestController is used to tell that the response sent from your controller should be sent to the browser, usually an object mapped to json.
It is the same as adding #ResponseBody.

Although you just got the answer. However, your post has two points.
Firstly, configure Freemarker template in Spring Boot quite easy. No need to use WebMvcConfigurerAdapter. You just need to place your properties on your class path with the content below
spring.freemarker.template-loader-path: /templates
spring.freemarker.suffix: .ftl
Secondly, #Controller is used to annotated classes as Spring MVC Controller. #RestController annotated classes are the same as #Controller but the #ResponseBody on the handler methods are implied. So you must use #Controller in your case.
Found this from the post Spring Boot FreeMarker Hello World Example

Related

Is greeting view and the result view pointing to the same object?

Here our controller may return one of two views.
In such a case where these 2 method signatures both contained Model Model Map and ModelAttribute, do the views share access to the Model and ModelAttribute loaded by a previous request handle?
#Controller
public class GreetingController {
#GetMapping("/greeting")
public String greetingForm(Model model) {
model.addAttribute("greeting", new Greeting());
return "greeting";
}
#PostMapping("/greeting")
public String greetingSubmit(#ModelAttribute Greeting greeting) {
return "result";
}
}
It does not point the same object.
I am assuming you are using https://spring.io/guides/gs/handling-form-submission/
and its code naming convention is quite confusing.
Please see the following test code.
I changed URL, variable name purposely.
Greeting.java
public class Greeting {
private long id;
private String content;
//... getters and setters
}
Greeting2.java
//created for testing
public class Greeting2 {
private long id;
private String content;
//... getters and setters
}
GreetingController.java
#Controller
public class GreetingController {
#GetMapping("/greeting") // greeting URL and GET request method
public String greetingForm(Model model) {
// th:object="${foo}" in template and thymeleaf
model.addAttribute("foo", new Greeting());
return "greeting_tmpl"; // src/main/resources/templates/greeting_tmpl.html
}
#PostMapping("/greeting_post")
public String greetingSubmit(#ModelAttribute Greeting2 bar) {
//I expected using bar variable in result_tmpl, but it used Greeting2(lowercase) as variable
return "result_tmpl"; // src/main/resources/templates/result_tmpl.html
}
}
src/main/resources/templates/greeting_tmpl.html
...
<body>
<h1>Form</h1>
<form action="#" th:action="#{/greeting_post}" th:object="${foo}" method="post">
<p>Id: <input type="text" th:field="*{id}" /></p>
<p>Message: <input type="text" th:field="*{content}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
src/main/resources/templates/result_tmpl.html
...
<body>
<h1>Result</h1>
<p th:text="'id: ' + ${greeting2.id}" /> <!-- this name should be like bar.id not greeting2 -->
<p th:text="'content: ' + ${greeting2.content}" />
Submit another message
</body>
</html>
Simply,
Browser triggers #GetMapping.
Server Parses Greeting model to HTML form values in greeting template and response to the browser.
Submit Form Data using POST method triggers #PostMapping.
#ModelAttribute Greeting2(Can be any model which can parse form values(in this case, id,content) will parse form values to Greeting2 model.
Server Parses Greeting2 model to HTML form values in greeting template and response to the browser.

Retrieve data from ModelAttribute method in the view

In my current spring project, I have a generic controller like this:
public class basicController<E> {
#Autowired
private basicService<E> serv;
protected Class<?> clazz;
public basicController(Class<?> clazz) {
this.clazz = clazz;
}
...
}
with a method like this:
#ModelAttribute("lista")
public List<E> populateList() {
return serv.lista();
}
I wonder if it's possible use the value for lista in a structure like that (in the html page):
<select class="form-control" th:name="...">
<option th:each="opt : ${lista}" th:value="${opt.getId()}"><span th:text="${opt}"/>
</option>
</select>
this page is mapped in the controllers with methods like that:
generic controller
#RequestMapping(value = "cadastra")
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
#Menu(label = "cadastra")
public String cadastra(Model model) throws Exception {
model.addAttribute("command", serv.newObject());
return "private/cadastra";
}
home controller (contains mappings for public views, among others things)
#RequestMapping(value = "/settings")
public String settings(Model model) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("usuario", auth.getName());
model.addAttribute("menu", MenuList.index());
model.addAttribute("settings", MenuList.settings());
return "private/settings";
}
#RequestMapping(value = "/profile")
public String profile(Model model) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("usuario", auth.getName());
model.addAttribute("menu", MenuList.index());
model.addAttribute("command", usuario(auth.getName()));
return "private/profile";
}
Anyone have any idea about this?
Ok, I just test and verify no extra configuration is needed for use the value from a ModelAttribute method. So, I just add methods like this in my controller:
#ModelAttribute("lista")
public List<E> populateListPagina() {
return serv.lista();
}
#ModelAttribute("classe")
public String getName() {
return clazz.getSimpleName();
}
and when I access any mapped view, I can use the value returned by this method in the way I like:
<tbody class="content">
<tr th:each="item : ${lista}">
<th></th>
<th th:each="coluna : ${campos}" th:text="${item[coluna]}"></th>
<th>
<div class="btn-group" role="group" aria-label="menu_item">
<button th:each="btn : ${menu_item}" type="button" class="btn btn-default link" th:attr="data-href=#{/__${classe}__/__${btn}__/__${item.getId()}__}" th:text="${btn}"></button>
</div>
</th>
</tr>
</tbody>
#ModelAttribute fires up before your controller methods do, and will disappear once your method runs I believe. So you won't have the object in the view anymore, it acts more like a #RequestParam.
However, you can try adding #SessionAttributes("lista") if you're using newer version of Spring ( I believe 4+). You have to be careful to make sure you close the session attributes though. To close, do what this guy did - link.

Getting => java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'enumLanguage' available as request attribute

I am using Spring form to get inputs from client (if i use normal html input). If i use Spring form input i got error : java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'enumLanguage' available as request attribute
this is my JSP:
<form:form commandname="enumLanguage" action="${pageContext.request.contextPath}/enumLanguage/create.action" method="post" modelAttribute="enumLanguage" >
<fieldset class="langStep">
<legend>Language Details</legend>
<table class="langpadding">
<tr>
<td><label>Name:</label></td>
<td><form:input path="name" cssClass="textbox2"></form:input></td>
<td><label class="llangpadding">Short Name:</label></td>
<td><form:input path="shortName" cssClass="textbox2"></form:input></td>
</tr>
</table>
Save<span class="icon icon3"></span>
</form:form>
and this is my Controller:
#RequestMapping( value="/enumLanguage/create.action", method=RequestMethod.POST)
public ModelAndView create(#ModelAttribute EnumLanguage enumLanguage) throws Exception {
ModelAndView mvc = null;
try{
List<EnumLanguage> enumLanguages = new ArrayList<EnumLanguage>();
enumLanguages.add(enumLanguage);
List<EnumLanguage> enumLanguagesList = enumLanguageService.create(enumLanguages);
mvc = new ModelAndView("setup/EnumLanguageList");
} catch (Exception e) {
}
return mvc;
}
Make sure you have your #ModelAttribute set to the model when rendering the view
Make sure you made available in the view a model attribute with a key enumLanguage which is the value of the commandname of the form.
So the controller method that returns the view containing the form that you posted should look something like this.
#RequestMapping(value = "/language-details.do", method = RequestMethod.GET)
public ModelAndView initLanguageDetailsView() {
ModelMap model = new ModelMap();
EnumLanguage enumLang = new EnumLanguage();
//setters blah blah
//...
//make it available to the view
model.addAttribute("enumLanguage", enumLang);
return new ModelAndView("language-details", model);
}

Redirect on Spring Tiles Causing Parameters to be Appended to the URL

I am using Spring 3 and Tiles 3. Below is just a simplified example I made. I have a test controller where I list all the SimpleEntity objects. And there is an input field on the JSP to add a new entity via a POST. Here is the controller.
#Controller
#RequestMapping(value="/admin/test")
public class TestAdminController {
private String TEST_PAGE = "admin/test";
#Autowired
private SimpleEntityRepository simpleEntityRepository;
#ModelAttribute("pageName")
public String pageName() {
return "Test Administration Page";
}
#ModelAttribute("simpleEntities")
public List<SimpleEntity> simpleEntities() {
return simpleEntityRepository.getAll();
}
#RequestMapping(method=RequestMethod.GET)
public String loadPage() {
return TEST_PAGE;
}
#RequestMapping(method=RequestMethod.POST)
public String addEntity(#RequestParam String name) {
SimpleEntity simpleEntity = new SimpleEntity();
simpleEntity.setName(name);
simpleEntityRepository.save(simpleEntity);
return "redirect:/" + TEST_PAGE;
}
}
Everything works fine. However, when I submit the form, the URL adds the pageName parameter, so it goes from /admin/test to /admin/test?pageName=Test+Administration+Page. Is there anyway to prevent this from happening when the page reloads?
UPDATE
Here is the JSP form.
<form:form action="/admin/test" method="POST">
<input type="text" name="name" />
<input type="submit" name="Save" />
</form:form>

Spring framework bind form array property

// my form
public class myForm {
private double[] myField;
public double[] getMyField(){
return myField;
}
public void setMyField(double[] myField){
this.myField = myField;
}
}
// my jsp
...
...
<c:set var="i" value="0"/>
<c:forEach items="${myList}" var="data">
<form:input path="myField[${$i}]"/>
<c:set var="i">${i + 1}</c:set>
</c:forEach>
...
...
After spring render jsp generate this code ;
<input type="text" value="0.0" name="myField0" id="myField0"/>
<input type="text" value="0.0" name="myField1" id="myField1"/>
<input type="text" value="0.0" name="myField2" id="myField2"/>
...
...
Spring cant bind my form on controller , because form names not valid (myField0, myField1..) . If i change names with firebug (as myField[0], myField[1] etc.) initBinder works and i catch my form data on controller. How can i solve this?
Thanks.
Use a Collection in your form instead of an array :
public class myForm {
private Collection<Double> myField;
public Collection<Double> getMyField(){
return myField;
}
public void setMyField(Collection<Double> myField){
this.myField = myField;
}
}

Resources