Use a single freemarker template to display tables of arbitrary pojos - freemarker

Attention advanced Freemarker gurus:
I want to use a single freemarker template to be able to output tables of arbitrary pojos, with the columns to display defined separately than the data. The problem is that I can't figure out how to get a handle to a function on a pojo at runtime, and then have freemarker invoke that function (lambda style). From skimming the docs it seems that Freemarker supports functional programming, but I can't seem to forumulate the proper incantation.
I whipped up a simplistic concrete example. Let's say I have two lists: a list of people with a firstName and lastName, and a list of cars with a make and model. would like to output these two tables:
<table>
<tr>
<th>firstName</th>
<th>lastName</th>
</tr>
<tr>
<td>Joe</td>
<td>Blow</d>
</tr>
<tr>
<td>Mary</td>
<td>Jane</d>
</tr>
</table>
and
<table>
<tr>
<th>make</th>
<th>model</th>
</tr>
<tr>
<td>Toyota</td>
<td>Tundra</d>
</tr>
<tr>
<td>Honda</td>
<td>Odyssey</d>
</tr>
</table>
But I want to use the same template, since this is part of a framework that has to deal with dozens of different pojo types.
Given the following code:
public class FreemarkerTest {
public static class Table {
private final List<Column> columns = new ArrayList<Column>();
public Table(Column[] columns) {
this.columns.addAll(Arrays.asList(columns));
}
public List<Column> getColumns() {
return columns;
}
}
public static class Column {
private final String name;
public Column(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class Person {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
public static class Car {
String make;
String model;
public Car(String make, String model) {
this.make = make;
this.model = model;
}
public String getMake() {
return make;
}
public String getModel() {
return model;
}
}
public static void main(String[] args) throws Exception {
final Table personTableDefinition = new Table(new Column[] { new Column("firstName"), new Column("lastName") });
final List<Person> people = Arrays.asList(new Person[] { new Person("Joe", "Blow"), new Person("Mary", "Jane") });
final Table carTable = new Table(new Column[] { new Column("make"), new Column("model") });
final List<Car> cars = Arrays.asList(new Car[] { new Car("Toyota", "Tundra"), new Car("Honda", "Odyssey") });
final Configuration cfg = new Configuration();
cfg.setClassForTemplateLoading(FreemarkerTest.class, "");
cfg.setObjectWrapper(new DefaultObjectWrapper());
final Template template = cfg.getTemplate("test.ftl");
process(template, personTableDefinition, people);
process(template, carTable, cars);
}
private static void process(Template template, Table tableDefinition, List<? extends Object> data) throws Exception {
final Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("tableDefinition", tableDefinition);
dataMap.put("data", data);
final Writer out = new OutputStreamWriter(System.out);
template.process(dataMap, out);
out.flush();
}
}
All the above is a given for this problem. So here is the template I have been hacking on. Note the comment where I am having trouble.
<table>
<tr>
<#list tableDefinition.columns as col>
<th>${col.name}</th>
</#list>
</tr>
<#list data as pojo>
<tr>
<#list tableDefinition.columns as col>
<td><#-- what goes here? --></td>
</#list>
</tr>
</#list>
</table>
So col.name has the name of the property I want to access from the pojo. I have tried a few things, such as
pojo.col.name
and
<#assign property = col.name/>
${pojo.property}
but of course these don't work, I just included them to help convey my intent. I am looking for a way to get a handle to a function and have freemarker invoke it, or perhaps some kind of "evaluate" feature that can take an arbitrary expression as a string and evaluate it at runtime.

?eval is (almost?) always a bad idea, because it often comes with performance drawbacks (e.g. a lot of parsing) and security problems (e.g. "FTL injection").
A better approach is using the square bracket syntax:
There is an alternative syntax if we want to specify the subvariable name with an expression: book["title"]. In the square brackets you can give any expression as long as it evaluates to a string.
(From the FreeMarker documentation about retrieving data from a hash)
In your case I'd recommend something like ${pojo[col.name]}.

Found the answer.
${("pojo." + col.name)?eval}

Related

EL1008E: Property or field 'applicationName' cannot be found on object of type 'java.lang.String' - maybe not public or not valid?

Been stuck with this error for a while now.
EL1008E: Property or field 'applicationName' cannot be found on object of type 'java.lang.String' - maybe not public or not valid?
I'm trying to get the records from database through thymeleaf, but always gets this error. I think I've declared everything that is needed this.
Here's my code
Model
#Entity
#Table(name = "sms_alert", schema = "public")
public class SmsAlert {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sms_id")
private Long smsId;
#Column(name = "application_name")
private String applicationName;
#Column(name = "sms_flag")
private String smsFlag;
public SmsAlert() {
}
public SmsAlert(String applicationName, String smsFlag) {
super();
this.applicationName = applicationName;
this.smsFlag = smsFlag;
}
public Long getSmsId() {
return smsId;
}
public void setSmsId(Long smsId) {
this.smsId = smsId;
}
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public String getSmsFlag() {
return smsFlag;
}
public void setSmsFlag(String smsFlag) {
this.smsFlag = smsFlag;
}
}
Controller
#GetMapping(value = "/smsAlert/{applicationName}")
public String viewApplicationSmsAlert(#PathVariable("applicationName") String applicationName, Model model) {
System.out.println(applicationName);
model.addAttribute("sms", smsService.findSmsFlagByAppName(applicationName));
return "test";
}
Repository
#Repository
public interface SmsRepository extends JpaRepository<SmsAlert, Long> {
#Query(value = "SELECT s.application_name, s.sms_flag FROM public.sms_alert s WHERE s.application_name= :applicationName ", nativeQuery = true)
public String findSmsFlagByAppName(#Param("applicationName") String applicationName);
Service
#Service
public class SmsService {
#Autowired
private SmsRepository smsRepository;
public List<SmsAlert> findAll() {
return smsRepository.findAll();
}
public String findSmsFlagByAppName(String applicationName) {
return smsRepository.findSmsFlagByAppName(applicationName);
}
public void updateSmsFlag(String applicationName, String smsFlag) {
smsRepository.updateSmsFlag(applicationName, smsFlag);
}
}
HTML File
<body>
<div id="content">
<div>
<table class="table table-hover table-striped" id="invTable">
<thead>
<tr class="table-primary">
<th id="terminalId">Terminal ID</th>
</tr>
</thead>
<tbody>
<tr th:each="sms : ${sms}">
<td th:text="${sms.applicationName}"></td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
The findSmsFlagByAppName method in SmsRepository returns a String. Your controller is putting that String in the model with the variable name sms. Then in your view you are referencing ${sms.applicationName} which is trying to evaluate the applicationName property on that String, which of course does not exist.
You could make the error go away a number of ways. One is, populate the model withe applicationName as a String and then in the view simply refer to that String.
Another option is to rework the findSmsFlagByAppName method to return a SmsAlert.

Cannot convert MultipartFile into Blob in Spring

I am trying to save an uploaded file as Blob in a MySql record. I am new to Spring. When I am about to save the record after uploading a file, then when my POST method updateCandidate() executes, I get this exception:
Field error in object 'candidateForm' on field 'cv': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile#59c09df6]; codes [typeMismatch.candidateForm.cv,typeMismatch.cv,typeMismatch.java.sql.Blob,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [candidateForm.cv,cv]; arguments []; default message [cv]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.sql.Blob' for property 'cv'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.sql.Blob' for property 'cv': no matching editors or conversion strategy found]
What is going wrong? How to fix?
My entity:
import java.sql.Blob;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
#Entity
public class Candidate {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String ssn;
private String name;
private String surname;
private String technology;
private String media;
#Lob
private Blob cv;
private boolean activeCV;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public String getMedia() {
return media;
}
public void setMedia(String media) {
this.media = media;
}
public Blob getCv() {
return cv;
}
public void setCv(Blob cv) {
this.cv = cv;
}
public boolean isActiveCV() {
return activeCV;
}
public void setActiveCV(boolean activeCV) {
this.activeCV = activeCV;
}
}
In my service:
#Autowired
private CandidateRepository repository;
...
public Optional<Candidate> getCandidate(Long id){
return repository.findById(id);
}
public void addOrUpdateCandidate(Candidate candidate) {
repository.save(candidate);
}
In my controller:
#Controller
#RequestMapping("/candidates")
public class CandidateController {
#Autowired
private EntityManagerFactory emf;
#Autowired
private CandidateService service;
...
#GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(#PathVariable("id") Long id, Model model) {
Candidate candidate = service.getCandidate(id).get();
model.addAttribute("candidateForm", candidate);
return "updateCandidateForm";
}
#PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(#ModelAttribute("candidateForm") Candidate candidate, #RequestParam("cv") MultipartFile file) throws IOException {
InputStream iStream = file.getInputStream();
long size = file.getSize();
Session session = emf.unwrap(Session.class);
Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
}
My updateCandidateForm.jsp:
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<body>
<form:form method="POST" action="updateCandidateResult" modelAttribute="candidateForm" enctype="multipart/form-data">
<form:hidden path="id"/>
<table>
<tr>
<td><form:label path="name">Name</form:label></td>
<td><form:input path="name"/></td>
</tr>
<tr>
<td><form:label path="surname">Surname</form:label></td>
<td><form:input path="surname"/></td>
</tr>
<tr>
<td><form:label path="ssn">SSN</form:label></td>
<td><form:input path="ssn"/></td>
</tr>
<tr>
<td><form:label path="technology">Known Technology</form:label></td>
<td><form:input path="technology"/></td>
</tr>
<tr>
<td><form:label path="media">Found us on</form:label></td>
<td><form:input path="media"/></td>
</tr>
<tr>
<td><form:label path="cv">Select a cv</form:label></td>
<td><input type="file" name="cv" /></td>
</tr>
<tr>
<td><form:label path="activeCV">Active CV</form:label></td>
<td><form:checkbox path="activeCV" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit"/></td>
</tr>
</table>
</form:form>
</body>
In my POM:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
EDIT 1 (question):
Would there be even a way of preventing Spring from trying to convert MultipartFile to Blob at "Submit-time", triggering this operation sooner, having the POST method already manage the Candidate (with Blob field already set) object?
EDIT 2:
as suggested by JB Nizet, I tried to use a support POJO, which has the field CV of the type MultipartFile, to store temporarily what I post via form (text fields + file field), and I don't get that exception anymore, because at "Submit-time" the populated object has the cv field of the type of the uploaded file:
import org.springframework.web.multipart.MultipartFile;
public class CandidatePOJO {
private Long id;
private String ssn;
private String name;
private String surname;
private String technology;
private String media;
private MultipartFile cv;
private boolean activeCV;
#Override
public String toString() {
return "CandidatePOJO [id=" + id + ", ssn=" + ssn + ", name=" + name + ", surname=" + surname + ", technology="
+ technology + ", media=" + media + ", cv=" + cv + ", activeCV=" + activeCV + "]";
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public String getMedia() {
return media;
}
public void setMedia(String media) {
this.media = media;
}
public MultipartFile getCv() {
return cv;
}
public void setCv(MultipartFile cv) {
this.cv = cv;
}
public boolean isActiveCV() {
return activeCV;
}
public void setActiveCV(boolean activeCV) {
this.activeCV = activeCV;
}
}
in controller, now my first concern is to see whether the pojo can be correctly instantiated, so my GET-POST pair is:
#GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(#PathVariable("id") Long id, Model model) {
CandidatePOJO candidatePOJO = new CandidatePOJO();
candidatePOJO.setId(id);
model.addAttribute("candidateForm", candidatePOJO);
return "updateCandidateForm";
}
#PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(#ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) {
System.out.println("CANDIDATE POJO");
System.out.println(candidatePOJO.toString()); // here I notice id = null
/* MultipartFile to Blob conversion */
// MultipartFile file = candidatePOJO.getCv();
// InputStream iStream = file.getInputStream();
// long size = file.getSize();
// Session session = emf.unwrap(Session.class);
// Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
/* instantiating the entity object to be freezed in db */
// Candidate candidate = new Candidate();
// set all data from candidatePOJO..
// candidate.setCv(cv);
// service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
and I get a CandidatePOJO object with no id set. I don't manage to have the id pass from GET to POST. Does anyone know what is gong wrong?
EDIT 3:
after many days I opted for the solution with a support POJO, unbelievably I am noticing that the ID is passed from the GET method to the POST one (I haven't changed anything, I have just performed the classical Maven project cleaning as I did when I posted my issue). Unluckily I am facing now another exception (after restoring the code previously commented in the POST method, of course):
javax.persistence.PersistenceException: Hibernate cannot unwrap EntityManagerFactory as 'org.hibernate.Session'
How to fix that?
EDIT 4:
Exception just above solved replacing:
Session session = emf.unwrap(Session.class);
with:
EntityManager em = emf.createEntityManager();
Session session = (Session) em.getDelegate();
BY USING SUPPORT POJO, I finally solved. Indeed I had already solved earlier, by code. The code in the post is right (EDIT 2). When I often performed Maven project cleaning, this didn't discover the errors. I performed it in the last hours and unbelievably my code worked. I can't guess the magic :D I don't know what really happened. By code, I solved in two different ways. First:
#Autowired
private EntityManagerFactory emf;
// ........
#GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(#PathVariable("id") Long id, Model model) {
Candidate candidate = service.getCandidate(id).get();
CandidatePOJO candidatePOJO = new CandidatePOJO();
candidatePOJO.setId(id);
candidatePOJO.setName(candidate.getName());
candidatePOJO.setSurname(candidate.getSurname());
candidatePOJO.setSsn(candidate.getSsn());
candidatePOJO.setMedia(candidate.getMedia());
candidatePOJO.setTechnology(candidate.getTechnology());
candidatePOJO.setActiveCV(candidate.isActiveCV());
model.addAttribute("candidateForm", candidatePOJO);
return "updateCandidateForm";
}
#PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(#ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) throws IOException {
MultipartFile file = candidatePOJO.getCv();
InputStream iStream = file.getInputStream();
long size = file.getSize();
EntityManager em = emf.createEntityManager();
Session session = (Session) em.getDelegate();
Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
Candidate candidate = new Candidate();
candidate.setId(candidatePOJO.getId());
candidate.setName(candidatePOJO.getName());
candidate.setSurname(candidatePOJO.getSurname());
candidate.setSsn(candidatePOJO.getSsn());
candidate.setMedia(candidatePOJO.getMedia());
candidate.setTechnology(candidatePOJO.getTechnology());
candidate.setActiveCV(candidatePOJO.isActiveCV());
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
Second (with the same GET):
#PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(#ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) throws IOException, SerialException, SQLException {
MultipartFile file = candidatePOJO.getCv();
Blob cv = new SerialBlob(file.getBytes());
Candidate candidate = new Candidate();
candidate.setId(candidatePOJO.getId());
candidate.setName(candidatePOJO.getName());
candidate.setSurname(candidatePOJO.getSurname());
candidate.setSsn(candidatePOJO.getSsn());
candidate.setMedia(candidatePOJO.getMedia());
candidate.setTechnology(candidatePOJO.getTechnology());
candidate.setActiveCV(candidatePOJO.isActiveCV());
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
Being new to Spring, I don't know the differences yet. I would appreciate a lot an explanation. Moreover, I want to modify, sooner or later, this code, in order to get rid of the support POJO and to work with just the entity objects: if anyone can solve my initial issue, I would be forever grateful!

JSTL Items not getting displayed

have a very simple question on JSTL tags.
I am using spring to fetch data from back-end and display in the JSP.
Have the following code. I am getting the items of the for each tag printed in the console. But the same items are not getting printed/displayed inside the table rows.
<table id="table_id" class="display">
<thead>
<tr>
<th>Movie Name</th>
<th>Movie Rating</th>
</tr>
</thead>
<tbody>
<c:forEach items="${model.movieslist}" var="movie">
<tr>
<td>${movie.name}</td>
<td>${movie.rating}</td>
</tr>
</c:forEach>
</tbody>
My spring controller class
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
logger.info("returning hello view");
List<Movie> moviesList = DbManager.getInstance().getMovies();
ModelMap modelMap = new ModelMap();
modelMap.addAttribute("movieslist", moviesList);
return new ModelAndView("hello.jsp",modelMap);
}
Movie domain class
package springapp.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "movie")
public class Movie {
#Id
public String _id;
public String name;
public String getId() {
return _id;
}
public void setId(String _id) {
this._id = _id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public float rating;
public String date;
#Override
public String toString() {
// TODO Auto-generated method stub
return name;
}
}
First, your jsp should contain the following taglib
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
Second, you don't need to get it using ${model.moviesList}, modify it as follows:
<c:forEach items="${movieslist}" var="movie">

Model Attributes null with Thymeleaf-spring boot

I have a weird error that is driving me crazy.
I have a Controller with two different Get Methods - for two different url mappings.
URL1 works perfectly fine, with URL2, I get
[THYMELEAF][http-nio-8080-exec-3] Exception processing template
"questionnaireForm": Error during execution of processor
'org.thymeleaf.standard.processor.attr.StandardEachAttrProcessor'
(questionnaireForm:10)
caused by
java.lang.NullPointerException: null at org.thymeleaf.context.WebSessionVariablesMap.hashCode(WebSessionVariablesMap.java:276) ~[thymeleaf-2.1.4.RELEASE.jar:2.1.4.RELEASE]
After hours of checking each line, it's clear that my Controller Get Method is adding a non-null object to the Model.addAttribute before calling the view.
I've also removed all of my code from the template and just added the following to troubleshoot:
<tr th:each="var : ${#vars}">
<td th:text="${var.key}"></td>
<td th:text="${var.value}"></td>
This is returning the same null error as well!
The only thing i could find on the web was a bug with Thymeleaf 2.1.4 that was caused by not having a HttpSession Object
https://github.com/thymeleaf/thymeleaf/issues/349
but then again, my URL1 works fine!
I haven't posted any code here because I'm not sure what else to post, please ask!
EDIT: Added Controller Code. /admin/questions is URL1 that works fine, '/admin/questionnaires' is URL2 that doesn't work.
#Controller
public class AdminController {
private QuestionRepository questionRepository;
private EQuestionnaireRepository eQuestionnaireRepository;
private EQuestionnaire eQuestionnaire;
#Autowired
public AdminController(QuestionRepository questionRepository,
EQuestionnaireRepository
eQuestionnaireRepository){
this.questionRepository = questionRepository;
this.eQuestionnaireRepository = eQuestionnaireRepository;
eQuestionnaire = eQuestionnaireRepository.findAll().get(0);
}
#RequestMapping(value = "/admin/questions", method=RequestMethod.GET)
public String questionForm(Model model) {
model.addAttribute("question", new Question());
return "questionForm";
}//-->This Works fine
#RequestMapping(value = "/admin/questions", method=RequestMethod.POST)
public String saveQuestion(#ModelAttribute Question newQuestion, BindingResult bindingResult, Model model) {
if( bindingResult.hasErrors())
{
System.out.println(bindingResult);
System.out.println("BINDING RESULTS ERROR");
return "questionForm";
}
questionRepository.save(newQuestion);
return "questionForm";
}
#RequestMapping(value = "/admin/Questionnaires", method=RequestMethod.GET)
public String QuestionnaireForm(Model model){
model.addAttribute("ListofQ",eQuestionnaire.getQuestionList());
return "questionnaireForm";
UPDATE: I changed over to thymeleaf 2.1.5-SNAPSHOT, and now I'm not getting the previously mentioned error - but a different error:
There was an unexpected error (type=Internal Server Error, status=500).
Exception evaluating SpringEL expression: "ask.questionText"
(questionnaireForm:13)
So now, the object reference is null in the template. It's not null when i put it into the Model (i've got System.out of the getQuestionText method inside the GET method, so thats confirmed)
This is the simple code I'm trying in the template:
<table>
<tr th:each= "ask : ${ListofQ}"></tr>
<tr th:text= "${ask.questionText}"></tr>
</table>
Here is the Question Object:
#Entity
#Table(name = "Questions")
public class Question {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long ID;
private int sequence;
private String questionText;
private int weightage;
private String option1 = "option1";
private String option2 = "option2";
private String option3 = "option3";
public Question() {
}
public Long getID() {
return ID;
}
public void setID(Long iD) {
ID = iD;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
if (sequence > 0) this.sequence = sequence;
}
public String getQuestionText() {
return questionText;
}
public void setQuestionText(String questionText) {
this.questionText = questionText;
}
public int getWeightage() {
return weightage;
}
public void setWeightage(int weightage) {
this.weightage = weightage;
}
public void setOption1(String option1) {
this.option1 = option1;
}
public void setOption2(String option2) {
this.option2 = option2;
}
public void setOption3(String option3) {
this.option3 = option3;
}
public String getOption1() {
return option1;
}
public String getOption2() {
return option2;
}
public String getOption3() {
return option3;
}
output in "each" need to be in the same statement , i think this will work for you
<table>
<tr th:each= "ask : ${ListofQ}" th:text= "${ask.questionText}"></tr>
</table>
You are not returning the model object to the Thymeleaf.
Try something like this:
#RequestMapping(value = "/admin/Questionnaires", method=RequestMethod.GET)
public ModelAndView QuestionnaireForm(Model model){
return return new ModelAndView("questionnaireForm", "ListofQ",eQuestionnaire.getQuestionList());
}

Spring receive data from the client

Good evening!
public class Order {
private int idOrder;
private Basket basket;
// getter and setter
}
public class AnonymousOrder {
private String name;
private String telephone;
// getter and setter
}
public class UserOrder {
private User user;
// getter and setter
}
public class OrdersForm {
private List< ? extends Order> orders;
// getter and setter
}
#RequestMapping(value="/showOrders")
public String showOrders(Model model){
List<? extends Order> orders= adminManager.searchAllOrders();
OrdersShowForm ordersForm = new OrdersShowForm();
ordersForm.setOrders(orders);
model.addAttribute("ordersForm", ordersForm);
return "showOrders";
}
#RequestMapping(value="/showOrders", method = RequestMethod.POST)
public String showOrdersPOST(#ModelAttribute("ordersForm") OrdersShowForm ordersForm){
System.out.print(ordersForm);
return "showOrders";
}
<form:form modelAttribute="ordersForm">
<table class="features-table" border="1">
<c:forEach items="${ordersForm.orders}" var="order" varStatus="status">
<tr>
<c:if test="${order['class'].simpleName != 'UserOrder'}">
<td>
<input name="orders[${status.index}].name" value="${order.name}"/>
</td>
</c:if>
</c:forEach>
</table>
Problem: I am passing on page two types of data: UserOrder and AnonymousOrder, but when I try to get them on the server then come data type Order.
Question: How to transfer data to the server without changing their actual type?
P.S. sorry for my English)

Resources