Following are my POJO, Action, and JSP page.
For single instance of Expense I am getting proper input.
I would like to use ModelDriven to fetch multiple instance of Expense from JSP page.
For that I have created multiple <div class="Expense">...</div>, but I dont know what changes should I made in Action class.
How to achieve this?
Pojo Class:
public class Expense implements java.io.Serializable {
private Long id;
private Client client;
private String param;
private BigDecimal value;
private Date dt;
private Date adddate;
//getter and setter
}
Action Class:
public class ExpenxeAction extends ActionSupport implements ModelDriven<Expense> {
Expense e = new Expense();
ExpenseDAO dao = new ExpenseDAO();
private LoginCheck lc = null;
private List<Expense> expenseList = new ArrayList<Expense>();
public String insertExpense() {
dao.insert(e);
return SUCCESS;
}
#Override
public Expense getModel() {
return e;
}
}
JSP page:
<div class="Expense">
<label>Expense Type</label>
<input type="text" name="param" id="param"/>
<label>Amount</label>
<input type="text" name="value" class="form-control" id="value"/>
<label>Date</label>
<input type="text" name="dt" class="form-control" id="dt"/>
</div>
You should create a getter for expenseList
public List<Expense> getExpenseList() {
return expenseList;
}
in the JSP use indexed property names
<s:iterator value="expenseList" status="stat">
<div class="Expense">
<label>Expense Type</label>
<s:textfield name="expenseList[%{#stat.index}].param" id="param"/>
<label>Amount</label>
<s:textfield name="expenseList[%{#stat.index}].value" class="form-control" id="value"/>
<label>Date</label>
<s:textfield name="expenseList[%{#stat.index}].dt" class="form-control" id="dt"/>
</div>
</s:iterator>
To fetch array list of Expense objects in action class you need to make the following changes to your code.
First you have to change ModelDriven<Expense> to ModelDriven<List<Expense>> and remove Expense e = new Expense();, if you wish. So, finally your action class should look like this.
public class ExpenxeAction extends ActionSupport implements ModelDriven<List<Expense>>, Preparable {
ExpenseDAO dao = new ExpenseDAO();
private LoginCheck lc = null;
private List<Expense> expenseList = new ArrayList<Expense>();
public String insertExpense() {
for (Expense e : expenseList) { //loop to iterate over each elements
dao.insert(e);
}
return SUCCESS;
}
public List<Expense> getExpenseList() {
return expenseList;
}
public void setExpenseList(List<Expense> expenseList) {
this.expenseList = expenseList;
}
#Override
public void prepare() throws Exception {
expenseList = new ArrayList<Expense>();
}
#Override
public List<Expense> getModel() {
return expenseList;
}
}
Now, you have to make changes to your jsp page.
<div class="Expense">
<label>Expense Type</label>
<input type="text" name="model[0].param" id="param"/>
<label>Amount</label>
<input type="text" name="model[0].value" class="form-control" id="value"/>
<label>Date</label>
<input type="text" name="model[0].dt" class="form-control" id="dt"/>
<label>Expense Type</label>
<input type="text" name="model[1].param" id="param"/>
<label>Amount</label>
<input type="text" name="model[1].value" class="form-control" id="value"/>
<label>Date</label>
<input type="text" name="model[1].dt" class="form-control" id="dt"/>
<label>Expense Type</label>
<input type="text" name="model[2].param" id="param"/>
<label>Amount</label>
<input type="text" name="model[2].value" class="form-control" id="value"/>
<label>Date</label>
<input type="text" name="model[2].dt" class="form-control" id="dt"/>
</div>
You will notice that in the jsp code we have mentioned
model[index].<pojo_member_variable_name> instead of specifying of
lstExp[index].<pojo_member_variable_name>
This is because now we want struts2 to set the values directly into these member variables. So we have mentioned model[index].<pojo__member_variable_name> so that struts2 should automatically set the value into the list. Here in the above code we are creating list of 3 Expense Object. If you need to insert more values then just increment the index and set the values and remaining will be done by struts2 for you.
Related
I want to sent object with model.addAttribute and get all object's variable after saving.
Controller
#GetMapping(value = "/passport/details/{id}")
#PreAuthorize("isAuthenticated()")
public String passportDetails(Model model, #PathVariable(name = "id") Long id) {
PassportRequest passportRequest = businessService.getPassportRequestById(id);
model.addAttribute("passport", passportRequest);
return "/user/passport/create";
}
#PostMapping(value = "/passport/save")
#PreAuthorize("isAuthenticated()")
public String savePassport(PassportRequest passport) {
businessService.savePassportRequest(passport);
return "redirect:/passport/details/" + passport.getId();
}
html-code:
<form enctype="multipart/form-data" method="post" th:action="#{/passport/save}"
data-parsley-validate="" role="form"
novalidate="" th:object="${passport}" id="sign-form">
<input type="hidden" th:field="*{id}">
<input type="hidden" th:field="*{stage}">
<div class="col-6 m-input-group">
<label>Кем командирован</label>
<input type="text" name="postedBy" placeholder="Введите" th:field="*{postedBy}" required>
</div>
</form
The problem is I need to make <input type="hidden" th:field="*{objectVariable}"></input> to every variable to send them. Is there any easier way to do?
One way of doing this is annotating your controller with #SessionAttributes("passport") (which means it will store the object on the session and keep all it's properties during this controllers methods).
You might also need to change
public String savePassport(PassportRequest passport) {
to
public String savePassport(#ModelAttribute("passport") PassportRequest passport) {
to make sure all the attributes line up.
My Student class has list 'Objects'.
public class Student {
private List<Objects> list;
...
}
public class Objects {
private long id;
private String context;
...
}
#GetMapping("signup")
public String showSignUpForm(Model model) {
List<Objects> objectsList = new ArrayList<>();
objectsList.add(new Objects(1, "Math"));
objectsList.add(new Objects(2, "Phys"));
objectsList.add(new Objects(3, "Chem"));
objectsList.add(new Objects(4, "Geom"));
model.addAttribute("objects", objectsList);
model.addAttribute("student", new Student());
return "add-student";
}
#PostMapping("add")
public String addStudent(Student student, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-student";
}
return "redirect:list";
}
<form action="#" th:action="#{/students/add}" th:object="${student}" method="post">
<div class="group">
<div th:each="obj : ${objects}">
<input id="check" type="checkbox" th:field="*{list}" th:value="${obj}"/>
<label for="check" th:text="${obj.getContext()}"></label>
</div>
<div class="col-md-6">
<input type="submit" class="btn btn-primary" value="Submit">
</div>
</div>
</form>
So I want to add selected objects to student.list but I get this error:
Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.List' for property 'list'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'example.Objects' for property 'list[0]': no matching editors or conversion strategy found
How to do it right?
First of all, if you want to work with list you have to do it by index. So if you want to bind a list you have to do like below.
#GetMapping("signup")
public String showSignUpForm(Model model) {
List<Objects> objectsList = new ArrayList<>();
objectsList.add(new Objects(1, "Math"));
objectsList.add(new Objects(2, "Phys"));
objectsList.add(new Objects(3, "Chem"));
objectsList.add(new Objects(4, "Geom"));
List<Objects> emptyList = new ArrayList<>();
Student student = new Student();
student.addList(emptyList);
model.addAttribute("objects", objectsList);
model.addAttribute("student", student);
return "add-student";
}
<form action="#" th:action="#{/students/add}" th:object="${student}" method="post">
<div class="group">
<div th:each="obj,iterator : ${objects}">
<input id="check" type="text" th:field="*{list[${iterator.index}].id}" th:value="${obj.id}"/>
<input type="text" th:field="*{list[${iterator.index}].context}" th:value="${obj.context}"/>
</div>
<div class="col-md-6">
<input type="submit" class="btn btn-primary" value="Submit">
</div>
</div>
</form>
Secondly, if you want to do select and add then you have to use javascript. You have to track the index of the list and bind the value separately (id and context) in the correct index. You have to also consider to remove object from the list for unselection.
I have this input:
Masa: <input type="number" class="form-control form-text" name="masa"/>
<div class="text col-sm-12 error" th:if="${wzrost}" >
<p class="text text-center">
To pole jest wymagane
</p>
</div>
Wzrost: <input type="number" class="form-control form-text " name="wzrost"/>
<div class="text col-sm-12 error" th:if="${wzrost}" >
<p class="text text-center">
To pole jest wymagane
</p>
</div>
And this controller;
String x = String.valueOf(masa);
String y = String.valueOf(wzrost);
if(x==null ){
model.addAttribute("wzrost",true);
return"views/success";
}
if(y==null ){
model.addAttribute("wzrost",true);
return"views/success";
}
When I click form submit button I always get error nullpointerexception.
How do I validate input, so that when it is empty the message pops up
#PostMapping("/cal-bmi")
public String calculateBmiForm(Model model, Integer masa, Integer wzrost) {
String x = String.valueOf(masa);
String y = String.valueOf(wzrost);
if(x==null ){
model.addAttribute("wzrost",true);
return"views/success";
}
if(y==null ){
model.addAttribute("wzrost",true);
return"views/success";
}
}
ANd when i get a valu form masa and wzrost i check from null, i click submit alwas nullpointerexception
<form th:action="#{/cal-bmi}" method="post">
<ul class="gender-options">
<input id="man" type="radio" name="gender" value="male" required />
<label for="man">mężczyzna</label> ⁄
<input id="woman" type="radio" name="gender" value="female"/>
<label for="woman">kobieta</label>
</ul>
Masa: <input type="number" class="form-control form-text" required placeholder="(kg)" name="masa"/>
<!--<div class="text col-sm-12 error" th:if="${wzrost}">-->
<!--<p class="text text-center">-->
<!--To pole jest wymagane-->
<!--</p>-->
<!--</div>-->
Wzrost: <input type="number" class="form-control form-text " required placeholder="(cm)" name="wzrost"/>
<!--<div class="text col-sm-12 error" th:if="${wzrost}">-->
<!--<p class="text text-center">-->
<!--To pole jest wymagane-->
<!--</p>-->
<!--</div>-->
<input type="submit" class="col-lg-10 btn btn-primary" value="Oblicz"/>
</form>
Now i used required but is not good solution
It seems like you want to implement server side validation. For this the best approach is to use validators and its bindingResult. Steps to implement server side validation is
Have for model
public class PersonForm {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Use form model in html
<form action="#" th:action="#{/personForm}" th:object="${personForm}" method="post">
<table>
<tr>
<td><label th:text="#{label.name}+' :'"></label></td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Generic Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
Have validator class
#Component
public class PersonFormValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return PersonForm.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "name", "field.name.empty");
PersonForm p = (PersonForm) target;
if (p.getName().equalsIgnoreCase("XXX")) {
errors.rejectValue("name", "Name cannot be XXX");
}
}}
Bind validator to controller and let spring do the magic.
#Controller
public class WebController {
#Autowired
PersonFormValidator personFormValidator;
#InitBinder("personForm")
protected void initPersonFormBinder(WebDataBinder binder) {
binder.addValidators(personFormValidator);
}
#PostMapping("/personForm")
public String checkPersonInfo(#Validated PersonForm personForm, BindingResult bindingResult, final RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
return "personForm";
}
redirectAttributes.addFlashAttribute("personResult", apiClientService.getPersonResult(personForm));
return "redirect:/spouseForm";
}
}
i am new to spEL and i want to use the "SELECT COUNT(*) FROM Exams;" query with JPQL or spEL to check if the record in the database is less than 6(six);
This is what i have. I can not really figure out how to go about it because i am not able to use the #Query annotation in the controller.
//Problem writing spEL
<div class="form-group" th:if="${exams.count(*) < 6}" >
<form method="post" th:object="${newExam}" th:action="#{/exams}" class="inline new-item">
<label class="form-control input-sm"><input type="text" th:field="*{indexNumber}" placeholder="Index Number" autocomplete="off"/></label>
<select th:field="*{grade}" class="form-control input-sm">
<option value="" disabled="disabled">[Select Grade]</option>
<option th:each="grade : ${grades}" th:value="${grade.values}" th:text="${grade.name}">Grade</option>
</select>
<label class="form-control input-sm"><input type="text" th:field="*{courseOffered}" placeholder="CourseOffered" autocomplete="off"/></label>
<label class="form-control input-sm"><input type="text" th:field="*{examType}" placeholder="ExamType" autocomplete="off"/></label>
<label class="form-control input-sm"><input type="text" th:field="*{subject}" placeholder="Subject" autocomplete="off"/></label>
<label class="form-control input-sm datepicker"><input type="text" th:field="*{gradeYear}" placeholder="ExamYear" autocomplete="off"/></label>
<button type="submit" class="btn btn-primary">Add</button>
</form>
#Controller
public class ExamsController {
#Autowired
private ExamService examService;
#Autowired
private UserService userService;
#RequestMapping(path = "/cert_prog")
public String examsList(Model model){
Iterable<Exams> exams = examService.findAll();
model.addAttribute("exams", exams);
model.addAttribute("newExam", new Exams());
model.addAttribute("grades", Grade.values());
return "cert_prog";
}
#RequestMapping(path = "/mark", method = RequestMethod.POST)
public String toggleComplete(#RequestParam Long id) {
Exams exams = examService.findOne(id);
examService.toggleComplete(id);
return "redirect:/cert_prog";
}
#RequestMapping(path = "/exams", method = RequestMethod.POST)
public String addTask(#ModelAttribute Exams exams, Principal principal){
//User user = userService.findByUsername(principal.getName());
User user = (User)((UsernamePasswordAuthenticationToken)principal).getPrincipal();
exams.setUser(user);
//examService.increment(exams);
examService.save(exams);
return "redirect:/cert_prog";
}
}
#Service
public class ExamServiceImpl implements ExamService {
#Autowired
private ExamsDao examsDao;
#Override
public Iterable<Exams> findAll() {
return examsDao.findAll();
}
#Override
public Exams findOne(Long id) {
return examsDao.findOne(id);
}
#Override
public void toggleComplete(Long id) {
Exams exams = examsDao.findOne(id);
exams.setComplete(!exams.isComplete());
examsDao.save(exams);
}
#Override
public void save(Exams exams) {
examsDao.save(exams);
}
#Override
public void increment(Exams exams) {
//exams.setCounter(exams.getCounter() + 1);
}
You don't need to query the DB again. Exams is already an iterable collection of all exam records, isn't it? Try
<div class="form-group" th:if="${exams.size() < 6}" >
As long as the underlying class of the Iterable object implements the size() method, thymeleaf should be able to find and call it. Otherwise you'll need to cast to a collection that does implement size before you add the object to the model.
I have a form where I add the customer's informations. This informations are passed to #Controller by an Ajax call.
Customer.java
public class Customer {
private String name;
private String fiscalCode;
private String vat;
private String telephone;
private String webSite;
private String sector;
private String address;
//Below there are constructor and getter/setter methods
Above the form there is:
<c:set var="serverUrl" value="${pageContext.servletContext.contextPath}"/>
<script>
var serverUrl = '${serverUrl}';
</script>
Form in the jsp
<form>
<div class="form-group">
<input id="nameCustomer" class="form-control" type="text" placeholder="Name customer">
</div>
<div class="form-group">
<input id="fiscalCode" class="form-control" type="text" placeholder="Fiscal code">
</div>
<div class="form-group">
<input id="vat" class="form-control" type="number" placeholder="VAT number (if available)">
</div>
<div class="form-group">
<input id="telephone" class="form-control" type="tel" placeholder="Phone number">
</div>
<div class="form-group">
<input id="website" class="form-control" type="email" placeholder="Customer's Website (if available)">
</div>
<div class="form-group">
<input id="address" class="form-control" type="text" placeholder="Customer's Address">
</div>
<div class="form-group">
<input id="sector" class="form-control" type="text" placeholder="Sector">
</div>
<button id="createCustomer" type="button" class="btn btn-success" style="text-align: center">Save</button>
</form>
Ajax call (the link to this ajax call code is below the form)
$("#createCustomer").click(function () {
alert("createCustomer");
alert(serverUrl);
var nameCustomer = $("#nameCustomer").val();
var fiscalCode = $("#fiscalCode").val();
var vat = $("#vat").val();
var telephone = $("#telephone").val();
var website = $("#website").val();
var address = $("#address").val();
var sector = $("#sector").val();
alert(address);
$.ajax({
url: serverUrl + "/addCustomer",
dataType: 'text',
data: {name: nameCustomer,
fiscalCode: fiscalCode,
vat: vat,
telephone: telephone,
webSite: website,
address: address,
sector: sector},
success: function (data) {
$("#customerAdded").modal('show');
},
error: function (xhr, error, exception) {
$("#errorCustomer").modal('show');
}
});
});
Controller
#Controller
public class CustomerController {
#RequestMapping("addCustomer")
public void addCustomer(#ModelAttribute Customer customer){
JOptionPane.showMessageDialog(null, customer.toString());
}
Chrome gives me this error:
http://localhost:8080/ReportVisitaWeb/addCustomer?name=gdg&fiscalCode=dfgdfg&vat=&telephone=dfgg&webSite=dfggf&address=dfgddf§or=gdg Failed to load resource: the server responded with a status of 404 (Not Found)
Why?
You are not mapping the request "addCustomer" correctly. Edit your CustomerController as below:
#Controller
public class CustomerController {
#RequestMapping("/addCustomer")
public void addCustomer(#ModelAttribute Customer customer){
JOptionPane.showMessageDialog(null, customer.toString());
}
}