I'm trying to understand the concept of data binding in Spring-MVC with Velocity (I'm learning this framework and porting an app to this platform).
I'm used to getting form variables using request.getParameter("username"), in the Spring world it seems that I can perform validation and such against "form objects" e.g. a datamodel style object that represent all the fields of a form.
The concept of a validator makes sense, but marshaling the data from a query string to these objects is fuzzy for me still. This is the concept of "Data Binding" correct?
If I'm correct to this point a few specific questions:
When a "binding" is made between a form variable (say "username" for example) and the the field of an object (say org.a.b.MyNewUserFormObj.username) is that "binding" a permanent definition such that all subsequent http posts of that form cause the username form variable to be assigned to org.a.b.MyNewUserFormObj.username?
How in the world do I accomplish the above binding definition? (if what I've said up to now is correct I feel like Costello in 'Who's on First', I don't even know what I just said!), I just need a conceptual picture.
Thanks for setting straight a brain gone astray.
There is no magic in data binding.
Actually, Spring simply populate properties of #ModelAttribute object with the values of request parameters with the corresponding names (in the simpliest case request parameter have the same name as a property, but nested properties are also supported).
So, if you have
<input type = "text" name = "firstName" />
and
public class Person {
private String firstName;
... getters, setters ...
}
you get a value from the form field.
Spring also provides convenient method for creating HTML forms. So, instead of creating form fields manually, you can write in JSP:
<form:form modelAttribute = "person" ...>
<form:input path = "firstName" />
</form:form>
or in Velocity (note that in this case <form> is created manually and property path is prefixed with the model attribute name):
<form ...>
#springFormInput("person.firstName" "")
</form>
Fields of the forms generated this way will be prepopulated with the values of the corresponding properties of the model attribute (that's why model attribute name is needed).
Related
I've been studying Spring for a week or so but I feel like I am missing something in regards to form handling.
For example, If I have a form that allows entering of Student data, and I have a Student bean defined like so:
public class Student {
private Integer age;
private String name;
private Integer id;
// and other getters and setters
}
Then I have a form:
<form:input path="name" />
<form:input path="age" />
<form:input path="id" />
and a Controller method that processes that post request:
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String add(#ModelAttribute Student student) {
//do stuff
}
What If I want to create a custom form that is not directly adding "Student" that contains all the fields for each attribute that Student has, but instead a custom form with some convoluted fields that looks nothing like the Student we defined in the Model. For example:
<form:input path="age" />
<form:input path="firstname" />
<form:input path="lastname" />
Note that firstname and lastname properties both do not exist in the Student class definition. I could concatenate these two values and put it in name.
For this case, do I have to create a new bean class to define the attributes of that form? This seems kind of redundant to me.
And is this, ViewModel thing, a solution?
Create a "form-backing object", ie a class specific to your form. Copy the values from this FBO to your entity class.
It's not redundant if your form is not the same as your db entity.
If there are lots of fields, use a tool like Dozer to automate this.
Storing age and full name in the database is a bad idea though. Storing birth date and first name/last name is better.
In this case you can form a JSON object with only the required fields and send it as a request body to the controller.
At client side-
On click of a save button, frame the json and send as post request
At server side use-
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String add(#RequestBody Student student) { }
You can ask Spring to automagically bind request parameters to fields of a model attribute, or you can do everything by hand. The classical way, would be to have a form bean that has the same fields as the HTML form, and let Spring populate it, then you build your object from that form bean. Unless you have good reasons for it, my advice is to tick to that method that cleanly decouples the view part (the form + the form bean) from the model part (your model bean)
But you also have other options :
directly get the request parameters with #RequestParameter("firstname"), etc. annotated parameters and manually use them in your model object : you do by hand what Spring gives you for free but get full control
use a custom data binder. It could work, but will defeat almost everything in Spring MVC data binding and validation, so I strongly advise you not to go that way.
I have a <form:form> in my jsp page with several <form:input> fields (so, fields are databound). Some of those fields are populated by user, but some, instead of waiting for user to enter some value, need to pre-populated with the value of parameter sent to this page from another jsp page, through the spring controller. How to write that parametar into <form:input> so user doesn't have to?
If I understand your question correctly, you want to prepopulate some fields with values already submitted previously.
All you need to understand is that with the Spring form taglib, Spring expects you to put a command object in the model and will bind the values in that command object to the form fields.
If you don't specify the key for this command object in the model, the taglib will look for it with the key "command". You can specify a different name though with the commandName attribute on the form tag, such as:
<form:form commandName="myModelObject">
Spring would now look for an object in the model named myModelObject to use to bind the form tags.
The other thing you then need to do is in the form tags in your Spring form, reference the fields in your command object. So, for example, let's say your command object has a field firstName, you'd have an input tag like this:
<form:input path="firstName" />
The path attribute tells the attribute what fields in the command object it should bind with.
So then you simply put the command object in your model with the appropriate fields prepopulated and the corresponding form fields will have those values prepopulated. Such as if you put an object in the model with the name myModelObject that has a field firstName, it will be prepopulated with whatever firstName is currently set to in that object.
I have a HashMap<Long, ClientProperties> that I'm putting on the ServletContext at startup.
//During application-startup:
//getProperties() returns HashMap<Long, ClientProperties>
context.setAttribute("clientProps", clientManager.getProperties());
ClientProperties is a POJO with 5 attributes that i need to access in my jsp.
Basicly I need to get the correct POJO (by HashMap-key) and access its properties in my jsp.
More spesific (for example purposes), one of the POJO attributes is clientLogo
In my jsp i now have:
<img src="<c:url value='/images/logo.png'/>" alt="Logo">
I need to replace the path to the logo-file with the clientLogo-property of the POJO.
The HashMap-key to use should be extracted from the User-object stored in the session. It can be retrieved like this: ${sessionScope['user'].clientId}
Any ideas?
Using struts2 and spring btw if that matters.
To get an attribute foo from the servlet context, you use the same syntax as to get it from the session, but replace sessionScope by applicationScope.
But you have so many nested things here that you should define variables:
<c:set var="map" value="${applicationScope['clientProps']}"/>
<c:set var="mapKey" value="${sessionScope['user'].clientId}"/>
<c:set var="pojo" value="${map[mapKey]}"/>
<c:set var="clientLogo" value="${pojo.clientLogo}"/>
<c:url value="${clientLogo}"/>
Note that this is typically the kind of hard work that you should not have to do in the view. Implement the retrieval of the image path in the controller, in Java, and make it available as a property of your action/form, and access it directly from your view.
I'm looking for a very simple form processing API for Java. Assuming the form input fields correspond to bean properties, and all beans have javax.Validation annotations, ideally the API would:
Display a bean as an html form
Populate the bean, including nested objects where applicable, using the request parameters
Validate the input using Validation annotation
If there is an error, display errors at top of form, and highlight error fields.
Additionally:
It would be nice if i didn't have to buy into a whole application framework, since I am working with a legacy app.
Allow configuration for more complicated use cases, but by default just use convention.
Bonus:
generates javascript client side validation too.
Note: If this requires several different libraries, that is fine too.
Update:
Since I never found what I was looking for, and migrating to Spring was not an option, I went ahead and rolled my own solution. It is, affectionately, called java in jails (loosely modeled on rails form processing). It gives you dead simple (and pretty) form creation, client and server side validation, and request parameter to object mapping. No configuration required.
Example Bean:
public class AccountForm {
#NotBlank(groups = RequiredChecks.class)
#Size(min = 2, max = 25)
private String name;
//...
}
Example Form:
<%# taglib uri="http://org.jails.org/form/taglib" prefix="s" %>
<s:form name="accountForm" action="/jails-demo/jails" label="Your Account Details" style="side">
<s:text name="name" label="Name" size="25" />
<s:text name="accountName" label="Account Name" size="15" />
...
</s:form>
Example Validation and Mapping:
SimpleValidator validator = new SimpleValidator();
if ("submit".equals(request.getParameter("submit"))) {
Map<String, List<String>> errors = validator.validate(AccountForm.class, request.getParameterMap());
if (errors != null) {
AccountForm account = validator.getMapper().toObject(AccountForm.class, request.getParameterMap());
//do something with valid account
} else {
SimpleForm.validateAs(AccountForm.class).inRequest(request).setErrors(errors);
//handle error
}
} else {
SimpleForm.validateAs(AccountForm.class).inRequest(request);
//forward to formPage
}
This is what the form looks like, with client side validation using jQuery (provided by Position Absolute):
I don't think you will find something that has most of this functionality and is not a framework.
I can recommend Spring MVC - you can plug it in easily in the legacy app. It supports all of the above.
Doing-it-yourself won't be that hard either:
use BeanUtils.populate(bean, request.getParameterMap()) to fill your object with the request parameters
use javax.validation.* manually - here is how. For each error add request attributes which you can later display as errors.
Note that either way you will have to write the html code manually.
How to use bean in JSP with only <jsp:useBean>, not MVC?
Assume you have a grade.txt file which contains following data:
Tom 90
Jerry 70
Katy 80
John 60
It asks you to create a bean named grade.java, a JSP page named graderesult.jsp, and a html page named gradecheck.html.
gradecheck.html provides a input textbox and a button submit, once you submit the name of the student, the graderesult.jsp will communicates with bean to show the name and the score corresponding to the person.
You can make use of <jsp:setProperty name="beanname" property="*" /> to "automatically" set all request parameters as bean properties matching the property name. As this is a typical homework question, I won't give complete code examples, but only hints:
Create a bean Grade with a property name.
Add a constructor which loads the data from the text file into a Map<String, Integer> property representing name-score pairs. Learn more about Java IO here and about Java Maps here.
Add a "special" getter getScore() which returns the score from the Map using the name as key.
Create a form with <input type="text" name="name"> in the gradecheck.html. Let the form submit to graderesult.jsp. The request method doesn't matter, I would prefer POST though.
In the graderesult.jsp use <jsp:useBean> to declare and instantiate the bean in request scope and use <jsp:setProperty> to "automatically" set all input values in the bean.
Use EL to show the name by ${grade.name} and the associated score by ${grade.score}.
Good luck.