I've been working on a play form for a couple of days and am extremely frustrated. I went through the documentation and used the "Play with Java" book, and created a userWeights model class:
public class UserWeights {
public HashMap<ServicesWeights, Float> userWeights = new HashMap<>();
#Constraints.Required
public String user;
#Constraints.Required
public String password;
public int sampleSize;
public int misSampleSize;
}
My controller:
public class Application extends Controller {
private static final Form<UserWeights> userWeightsForm = Form.form(UserWeights.class);
public static Result index() {
return ok(index.render("Your new application is ready."));
}
public static Result finder () {
return ok(finder.render(userWeightsForm));
}
public static Result runWithUserInput () {
Form<UserWeights> boundForm = userWeightsForm.bindFromRequest();
if (boundForm.hasErrors()) {
return badRequest(index.render("FAIL"));
}
UserWeights weights = boundForm.get();
if (weights.checkboxChoices.get(0) != null && weights.checkboxChoices.get(0).equals("1")) {
runMethodA();
} else if (weights.checkboxChoices.get(1) != null && weights.checkboxChoices.get(1).equals("2")) {
runMethodB();
}
return TODO;
}
And the view:
#(UserWeightsForm: Form[UserWeights])
#import helper._
#import helper.twitterBootstrap._
#main("finder") {
<h1>Please fill the required fields</h1>
#helper.form(action = routes.Application.runWithUserInput(), 'enctype -> "multipart/form-data") {
<fieldset>
<legend>Finder</legend>
#helper.inputText(UserWeightsForm("user"),'_label -> "User")
#helper.inputPassword(UserWeightsForm("password"), '_label -> "Password")
<br/>
<label for="checkboxInput">Input Type:</label><br/>
<span class="buttonSet" id="checkboxInput">
<input type = "checkbox" id = "genericCheckbox" name = 'checkboxChoices[0]' value = "1">
<label for = "genericCheckbox">Generic Sample</label>
<input type = "number" name = 'UserWeightsForm("sampleSize")' id = "genericInput" value = "#UserWeightsForm("sampleSize").value"><br/>
<input type = "checkbox" id = "misCheckbox" name = 'checkboxChoices[1]' value = "2">
<label for = "misCheckbox">MisSample</label>
<input type = "number" name = 'UserWeightsForm("misSampleSize")' id = "misInput" value = "#UserWeightsForm("misSampleSize").value"><br/>
</span>
What I want - if the first checkbox is selected, the user will fill in the sampleSize text input field, and that value will be bounded to the form, while the misSampleSize field will be blank/zero/whatever (in this case I'm not using it anyway). And vice versa.
The problem - when I check the checkbox and fill in the sample text input, it binds the value 0 to the form.
I've tried setting the value in the input tag to null, removing it completely and using the template helper (I prefer to avoid it because it's not as flexible). I can't understand why the number I enter into the text field is ignored, and my programs considers it to be 0.
So question 1: How do I stop the 0 value being populated?
question 2 How can I send a value for one of the text fields (sampleSize or misSampleSize), and leave the other one empty, without getting form errors?
Thanks!
I work in Scala, not Java. But you I think you need your input id and name fields to match your form field names. It uses those to bind. You don't show your UserWeightsForm, but you could try something like:
<input type="number" name='sampleSize' id="sampleSize" value="#UserWeightsForm("sampleSize").value.getOrElse("0")"><br/>
<input type="number" name='misSampleSize' id="misSampleSize" value="#UserWeightsForm("misSampleSize").value.getOrElse("0")"><br/>
I also use a "getOrElse" on the value in case (error perhaps) there is no value.
Related
I'm trying to display the contents of a List<> of objects that are linked to the currently logged in user on a jsp page. The object is successfully created and stored in the database with a user_id of the user who created it, and with a simple system.out.print debugger I can see its added to the List.
But when I try to display this on the NewFile.jsp webpage its as if the the List is empty. There is no error, instead the page is just blank as if there is no List to iterate through.
Skill method in UserController
#RequestMapping(value = "/skill", method = RequestMethod.POST)
public String skill(#ModelAttribute("skillForm") Skill skillForm, BindingResult bindingResult, Model model, Principal principal) {
skillValidator.validate(skillForm, bindingResult);
if (bindingResult.hasErrors()) {
return "skill";
}
// Adds skill object to database and adding it to Users skill list
String name = principal.getName();
skillService.save(skillForm, name);
User currentUser = userService.findByUsername(principal.getName());
currentUser.addSkill(skillForm);
// Debug here shows that the list contains correct data and the list size
System.out.println(skillForm.getSkillName());
System.out.println(skillForm.getCategory());
System.out.println(currentUser.getSkills().size());
// Attempting to pass the list to NewFile.jsp to be displayed
List<Skill> savedSkills = currentUser.getSkills();
for(int i = 0; i < savedSkills.size(); i++) {
model.addAttribute("skills", savedSkills);
/*model.addAttribute("currentUser", currentUser);*/
}
return "NewFile";
}
NewFile.jsp
<table>
<c:forEach var="o" items="${savedSkills}" >
<tr>
<td>Skill Name:<c:out value = "${o.skillName}"/></td>
<td>Category:<c:out value = "${o.category}"/> </td>
</tr>
</c:forEach>
</table>
There are some mistakes. First you don't need a loop.
List<Skill> savedSkills = currentUser.getSkills();
model.addAttribute("skills", savedSkills);
Second, your EL has the wrong argument name, just like the others had stated.
<c:forEach var="o" items="${skills}" >
You need to specify what skill to be added , you are adding the List as an attribute but you need to the object that's inside it
for(int i = 0; i < savedSkills.size(); i++) {
model.addAttribute("skills", savedSkills[i]);
/*model.addAttribute("currentUser", currentUser);*/
}
Try by adding the code model.addAttribute("savedSkills", savedSkills); before the return statement. You haven't add the model attribute named with "savedSkills".
I have an legacy structure I need to use to render a matrix (or grid) using thymeleaf in a Springboot project.
I have several models to represent it with additional info.
public class CeldaGrid {
private int valor;
//Removed additional fields
//Constructor
//Getters/Setter
public class MiGrid {
//Represent each column of the matrix
private Collection<CeldaGrid> celdaGridList;
//Constructor
//Getter/Setter
public class ContenedorGrid {
//Represent the matrix
private Collection<MiGrid> gridList = new ArrayList<MiGrid>();
//Constructor
//Getter/Setter
This is how I initialze, in this case is 3x3 matrix (it could be different size):
Collection<MiGrid> gridList = new ArrayList<MiGrid>();
// Row 1
MiGrid miGrid = new MiGrid();
Collection<CeldaGrid> celdaGridList = new ArrayList<CeldaGrid>();
CeldaGrid celdaGrid = new CeldaGrid();
celdaGrid.setValor(1);
celdaGridList.add(celdaGrid);
celdaGrid = new CeldaGrid();
celdaGrid.setValor(2);
celdaGridList.add(celdaGrid);
celdaGrid = new CeldaGrid();
celdaGrid.setValor(3);
celdaGridList.add(celdaGrid);
miGrid.setCeldaGridList(celdaGridList);
gridList.add(miGrid);
// Row 2
miGrid = new MiGrid();
celdaGridList = new ArrayList<CeldaGrid>();
celdaGrid = new CeldaGrid();
celdaGrid.setValor(4);
celdaGridList.add(celdaGrid);
celdaGrid = new CeldaGrid();
celdaGrid.setValor(5);
celdaGridList.add(celdaGrid);
celdaGrid = new CeldaGrid();
celdaGrid.setValor(6);
celdaGridList.add(celdaGrid);
miGrid.setCeldaGridList(celdaGridList);
gridList.add(miGrid);
ContenedorGrid contenedorGrid = new ContenedorGrid();
contenedorGrid.setGridList(gridList);
model.addAttribute("contenedorgrid", contenedorGrid);
and finally the page:
<form action="#" th:action="#{/}" th:object="${contenedorgrid}" method="post">
<table>
<tbody>
<tr th:each="eachCelda,indexList : *{gridList}">
<td th:each="celda,indexCelda: ${eachCelda.celdaGridList}">
<input type="text" th:id="${celda.valor}"
th:field="*{celdaGridList[__${indexCelda.index}__].valor}"/>
</td>
</tr>
</tbody>
</table>
</form>
This is the list with values:
[MiGrid [celdaGridList=[CeldaGrid [valor=1], CeldaGrid [valor=2], CeldaGrid [valor=3]]], MiGrid [celdaGridList=[CeldaGrid [valor=4], CeldaGrid [valor=5], CeldaGrid [valor=6]]]]
And this is the error:
org.springframework.beans.NotReadablePropertyException: Invalid
property 'celdaGridList[0]' of bean class
[org.cabildo.gestatur.model.grid.ContenedorGrid]: Bean property
'celdaGridList[0]' is not readable or has an invalid getter method:
Does the return type of the getter match the parameter type of the
setter?
Code:
https://www.dropbox.com/sh/ppyf3f0l6p3v2ig/AABjXsS_6Mu2nmKd-XBRTclua?dl=0
UPDATE 1:
Changed code from Collection to List, exactly the same error.
Any suggestion?
Thanks
You should change type of field celdaGridList to List<CeldaGrid> inside MiGrid class.
Collection isn't ordered and it has no method get(int index) so value from specific index can not be fetched. But you are trying to do so with the line th:field="*{celdaGridList[__${indexCelda.index}__].valor}
, what results in exception.
Update
I've looked closer at your page.
You've declared an object th:object="${contenedorgrid}" which is of type ContenedorGrid. Then you've used an asterix selector th:field="*{celdaGridList[__${indexCelda.index}__].valor}" on this object what is equivalent to th:field="${contenedorgrid.celdaGridList[__${indexCelda.index}__].valor}" what is obviously wrong because it misses gridList.
Please update th:field attribute within your <input> with the following code:
th:field="*{gridList[__${indexList.index}__].celdaGridList[__${indexCelda.index}__].valor}"
I am having one modelform
public class ABC
{
private String a;
private String b;
private Obj obj;
...getteres and setters.....
}
in my jsp my OnSubmit function is
function showMethod(rowid)
{
document.diamondSingleStoneLabForm.action = adminUrl + "/act.htm";
document.diamondSingleStoneLabForm.suid.value =value1;
document.diamondSingleStoneLabForm.obj.value = tempObj;
document.diamondSingleStoneLabForm.submit();
}
now, my question is
How to pass Object to specified action..???
Or How to pass obj.name property value (I cant access it using document.diamondSingleStoneLabForm.obj.name.value = nameVal;)..???
Hope in your jsp page you have a text field like this :
<input type="text" name="obj" value="<%= rs.getString(n)%>" >
but you should tally with the modelform & jsp(this will be your logical part) page where from you fetch the value like <%= rs.getString(n)%>
then you can do this thing
var obj = obj_value;
var action = adminUrl + "/act.htm";
document.diamondSingleStoneLabForm.action = action ;
document.diamondSingleStoneLabForm.submit();
Or you can use: jQuery.post() method in this format
jQuery.post( url [, data] [, success(data, textStatus, jqXHR)] [, dataType] )
you can take a look of this link: jQuery.post()
I have a model that contains a String and a list:
public String title;
public List<String> topics;
In index.scala.html I use a form to add new items:
#form(routes.Application.newPaper()) {
#inputText(paperForm("title"))
<input type="submit" value="Create">
}
with a simple String, this works nicely. But I would like to show checkboxes
#for(t <- topics) {
<input type='checkbox' name='topic' value=#t>#t <br>
}
and subsequently add all checked 'topics' to the List<String> topics; of my new item. How can I process the checkboxes within #form{ ... }?
I am using Play!Framework 2.1.0, below is the solution :
1. In the scala template, you must give all checkbox name like this:
#form(action = routes.Application.newPaper()) {
#inputText(paperForm("title"))
#******* Indexed chekbox name *********#
#for((t, index) <- topics.zipWithIndex) {
<input type="checkbox" name="topics[#index]" value="#t">#t <br>
}
<input type="submit" value="Create">
}
2. Then in your controller, as an action to handle form submit, you should doing something like this :
public static Result newPaper() {
// Bind submitted form value to your model, ex. Paper.java
Form<Paper> paperForm = Form.form(Paper.class).bindFromRequest();
Paper paper = paperForm.get();
Logger.info("Title entered = " + paper.title);
// Because in template we use indexed name, unchecked item are binded with null value
paper.topics.removeAll(Collections.singleton(null)); // remove value for unchecked topic
for (String t : paper.topics) {
Logger.info("The topic is " + t);
}
Logger.info("Total topic selected = " + paper.topics.size());
return redirect(routes.Application.index()); // redirect page
}
UPDATE
This is another idea to the solution. Your checkbox code on scala template is not modified.
#for(t <- topics) {
<input type='checkbox' name='topic' value=#t>#t <br>
}
So the controller should be like this :
public static Result newPaper() {
// Bind submitted form value to your model, ex. Paper.java
Form<Paper> paperForm = Form.form(Paper.class).bindFromRequest();
Paper paper = paperForm.get();
// get request value from submitted form
Map<String, String[]> map = request().body().asFormUrlEncoded();
String[] checkedVal = map.get("topic"); // get selected topics
// assign checked value to model
paper.topics = Arrays.asList(checkedVal);
// for debugging purpose
for (String t : paper.topics) {
Logger.info("The topic is " + t);
}
Logger.info("Total topic selected = " + paper.topics.size());
return redirect(routes.Application.index()); // redirect page
}
Hope this idea is more elegant.. :)
Note: I have tested on Play!Framework 2.1.1 too, and that is work for me.
I want to validate a field to be able to accept values only between 1 and 100. It works fine, but when i write a something that is not an integer is don't see the custom message i expect.
This is the field:
<h:inputText id="discountPercentage" value="#{newOfferSupportController.discountPercentage}" validator="#{newOfferSupportController.validateDiscountPercentage}"/>
<span style="color: red;"><h:message for="discountPercentage"
showDetail="true" /></span>
This is the validator method:
public void validateDiscountPercentage(FacesContext context,
UIComponent validate, Object value) {
FacesMessage msg = new FacesMessage("");
String inputFromField = "" + value.toString();
String simpleTextPatternText = "^([1-9]|[1-9]\\d|100)$";
Pattern textPattern = null;
Matcher productValueMatcher = null;
textPattern = Pattern.compile(simpleTextPatternText);
productValueMatcher = textPattern.matcher(inputFromField);
if (!productValueMatcher.matches()) {
msg = new FacesMessage("Only values between 1 and 100 allowed");
throw new ValidatorException(msg);
}
for (int i = 0; i < inputFromField.length(); i++) {
// If we find a non-digit character throw Exception
if (!Character.isDigit(inputFromField.charAt(i))) {
msg = new FacesMessage("Only numbers allowed");
throw new ValidatorException(msg);
}
}
}
This is the error i message i see when i ester something that is not a number:
Why i don't see the message: Only numbers allowed?
Why don't you use jsf's LongRangeValidator for this purpose?
<h:inputText id="discountPercentage"
value="#{newOfferSupportController.discountPercentage}">
<f:validateLongRange minimum="1" maximum="100"/>
</h:inputText>
You can even define your own custom validation messages if the default messages don't fit your needs. See this answer for more information.
Besides this, your error message is for productValue and not for discountPercentage.