View Full Version : Creating complex Domain Objects from Web MVC
pearl8
Feb 8th, 2006, 11:48 PM
Hi,
I need to display a person's information. The domain looks like this
Person
- firstName --> String
- lastName --> String
- address --> Address
Address
- line1 --> String
- line2 -->String
Now when this gets displayed on the web page it's all flat. But when the user clicks submit, I want to reconstruct the domain object graph. From what examples I've looked at I don't see any such way to do this. The Forms all seem pretty flat.
Please help!
EndlessWinter
Feb 9th, 2006, 04:58 AM
No problems, your <spring:bind> tags will have complex paths. Example:
<spring:bind path="command.person.address.line1">
<input type="text" id="startDate" name="<c:out value='${status.expression}'/>"
value="<c:out value='${status.value}'/>" />
</spring:bind>
pearl8
Feb 9th, 2006, 08:58 AM
Hi thanks for the reply. I am having some basic issues (I hope).
I have the following
public class Credentials {
private User username = new User();
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User getUsername() {
return username;
}
public void setUsername(User username) {
this.username = username;
}
}
public class User {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
In the jsp I have
<spring:bind path="credentials.username.username">
At runtime I see this error. Any idea how to avoid this?
Errors as string org.springframework.validation.BindException: BindException: 1 errors; Field error in object 'credentials' on field 'username': rejectedValue=[guest]; codes=[typeMismatch.credentials.username,typeMismatch.use rname,typeMismatch.com.devx.tradingapp.business.Us er,typeMismatch]; arguments=[(org.springframework.context.support.DefaultMessag eSourceResolvable)[codes=[credentials.username,username]; arguments=[null]; defaultMessage=[username]]]; defaultMessage=[Failed to convert property value of type [java.lang.String] to required type [com.devx.tradingapp.business.User] for property 'username']
Colin Yates
Feb 9th, 2006, 08:59 AM
Errors as string org.springframework.validation.BindException: BindException: 1 errors; Field error in object 'credentials' on field 'username': rejectedValue=[guest]; codes=[typeMismatch.credentials.username,typeMismatch.use rname,typeMismatch.com.devx.tradingapp.business.Us er,typeMismatch]; arguments=[(org.springframework.context.support.DefaultMessag eSourceResolvable)[codes=[credentials.username,username]; arguments=[null]; defaultMessage=[username]]]; defaultMessage=[Failed to convert property value of type [java.lang.String] to required type [com.devx.tradingapp.business.User] for property 'username']
You need to register a property editor to convert the String representation to a User.
BTW; are you sure you meant to do it this way; setting/getting the userNAME should probably not get/set the User.
pearl8
Feb 9th, 2006, 09:19 AM
Hm interesting. I just changed username to user in Credentials.java
public class Credentials {
private User user = new User(); //was originally private User username = new User();
and then changed the jsp to
<spring:bind path="credentials.user.username">
and the bind errors are not there, although credentials.getUser().getUsername() is still not reflecting the value in enter in the html form.
Colin Yates
Feb 9th, 2006, 09:20 AM
Are you binding to ".user.username"?
pearl8
Feb 9th, 2006, 09:24 AM
BTW; are you sure you meant to do it this way; setting/getting the userNAME should probably not get/set the User.
This example might seem a little illogical. I'm just trying a POC here, so please bear with me! One thing seems a little weird. shouldn't the getters/setters that I originally used getUsername and setUsername, work just fine?
pearl8
Feb 9th, 2006, 09:26 AM
yes i am binding to .user.username. again pardon my weird example. my original intent is to have a Person backing form that has the following
Person {
Address officialAddress;
Address homeAddress;
}
so the Credentials example is just a POC. I'm puzzled as to why changing username to user helped the bind error go away
to summarize:
trial 1:
public class Credentials {
private User username = new User();
<spring:bind path="credentials.username.username">
gave bind errors but
trial 2:
public class Credentials {
private User user = new User();
<spring:bind path="credentials.user.username">
gave no bind errors. but the value of user in credentials.getUser().getUsername() is still not showing up (one step at a time i guess!)
EndlessWinter
Feb 9th, 2006, 10:50 AM
So, you've filled in the form, and the try to retrieve the values in the controller, right?
Can you post the source of the controller then?
pearl8
Feb 9th, 2006, 10:54 AM
well actually i have a validator where i print it:
public class LogonValidator implements Validator {
public void validate(Object obj, Errors errors) {
System.out.println("Errors as string " + errors.toString() );
Credentials credentials = (Credentials) obj;
System.out.println("Creds " + credentials.getUser().getUsername() );
I get the credentials.getUser().getUsername() as null
pearl8
Feb 9th, 2006, 08:01 PM
just curious that even if i write property editors, how will a more complex case be handled? suppose i have many Strings that need to map to one domain object for instance person.address now Address can have a lot of sub fields like address line 1, address line 2, address line 3 etc. it also might have custom subobjects too.
EndlessWinter
Feb 10th, 2006, 05:06 AM
In fact, you might want not to write property editors.
1) Use SimpleFormController
2) Set controller's isSessionForm property to true
3) in formBackingObject create your command object and initialize all complex objects there
4) use comlpex paths in spring:bind tags
There shouldn't be any problems with such approach.
The example of custom editor usage is binding date properties
pearl8
Feb 10th, 2006, 05:25 AM
it would be awesome if you can attach some code on here if you got it to work without custom property editors.
i did have to write Custom property editors. It just refused to work without that and I was getting binding errors. It appears that for each domain object in the nested graph you need to write up one Custom property editor.
Now the issue I'm facing is that i'm wondering how you handle cases where you need to get in multiple values into a single nested domain object.
let's take the example of organization which has address
now address would have line1 line2 line3 which would be displayed as different ui fields in the html form and are Strings in the Address object
in order to populate the Address object when you write up a propertyeditor you need to pass the text value which is some sort of a concatenation from these individual fields to the method setAsText(String textValue) in the custom property editor
would this not get ugly?
EndlessWinter
Feb 10th, 2006, 10:15 AM
I haven't any problems with binding complex objects without editors. Just make sure, that graph is preinitialized (i.e. all complex objects must be created somewhere before you show the form, formBackingObject is the right place for this).
For example
<spring:bind path="subscriberForm.products[${st.count-1}].quantity">
<input id="quantity<c:out value='${st.count}'/>" type="text" name="<c:out value='${status.expression}'/>" value="<c:out value='${status.value}'/>" />
</spring:bind>
As you can see, i am bidning here fairly complex object, but it mean also, that my subscriberForm hast not null array of products, which is holding not null Products objects, which have quantity (it is primitive int) property
Colin Yates
Feb 10th, 2006, 10:33 AM
I think there is some confusion about what property editors do :)
First off; propertyEditors do not edit your properties; they convert a "complex" object to and from a String. Nothing more, nothing less.
Looking at a common example:
public class Address() {
String get/SetPostCode();
String get/SetCounty();
}
public class YourObject() {
Customer get/setCustomer();
Address get/setAddress();
}
Assuming yourObject.getAddress() will *never* return null, you do *not* need a property editor to set the value of yourObject.address.county or yourObject.address.postCode because these are just Strings.
You *do* need a Property editor to set yourObject.customer because Spring has no idea how to convert the String from the request parameter into the Customer object.
As for using a PropertyEditor to set yourObject.address given a postCode and a county; unfortunately you cannot :( Property Editors do *not* support more than a single string.
Does that clarify things? PropertyEditors really should be renamed propertyConvertors IMO :)
EndlessWinter
Feb 10th, 2006, 10:57 AM
There's no confusion about what property editors do :)
There's two different ways to construct the objects from form data. The first is to use property editors, and the second is to create this object prior the showing the form, and then bind form fields to these (already created, but empty) objects using complex paths. I prefer the second right now
Colin Yates
Feb 10th, 2006, 11:24 AM
There's no confusion about what property editors do :)
The discussion seems to be confused between *creating* objects (which PE do not do) and converting objects (which PE do do).
There's two different ways to construct the objects from form data. The first is to use property editors, and the second is to create this object prior the showing the form, and then bind form fields to these (already created, but empty) objects using complex paths. I prefer the second right now
These are not mutually exclusive; a PE could not construct an Address from three seperate request parameters; so the Address object must exist. On the other hand; if one of the Address fields was a yourdomain.Country object; you *would* use a property editor to convert the address.country String HTTPServletRequest parameter into a yourdomain.Country.
Like I said; thinking about PropertyEditors as *constructing* objects is a bit (IMO) misleading; they simply convert *existing* data. This of course, is utter rubbish; PE can create new data; but I find it helpful.
To clarify; PEs would not create a new Address field, but they would bind data into the existing address field :)
pearl8
Feb 10th, 2006, 11:30 PM
thanks for all your clarifications. i find that in the case of a complex object graph the setters in the nested objects are not being called. i tried this simple experiment. this is inspite of initializing the object graph in the formBackingObject
here is the example:
public class Credentials {
private String username;
private Pwdcreds pwdcreds = new Pwdcreds();
public Pwdcreds getPwdcreds() {
return pwdcreds;
}
public void setPwdcreds(Pwdcreds pwdcreds) {
this.pwdcreds = pwdcreds;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
public class Pwdcreds {
private String pwdcreds = "init";
public String getPwdcreds() {
return pwdcreds;
}
public void setPwdcreds(String pwdcreds) {
this.pwdcreds = pwdcreds;
}
}
<td width="33%" align="right">Password: </td>
<td width="66%" align="left">
<spring:bind path="credentials.pwdcreds.pwdcreds">
<input type="password" name="password" />
</spring:bind>
and in the controller:
protected Object formBackingObject(HttpServletRequest request) throws Exception {
Credentials creds = new Credentials();
return creds;
}
but still the pwdcreds String never gets populated with the user entered value. am i doing anything wrong?
Colin Yates
Feb 11th, 2006, 04:02 AM
<spring:bind path="credentials.pwdcreds.pwdcreds">
<input type="password" name="password" />
</spring:bind>
Almost ;)
The spring:bind is absolutely correct; the <input name="password".. isn't.
When you submit that form, a request parameter called "password" will be whose value is the value of the input element. The spring:bind tag above *won't* affect that. However the spring:bind will help you by giving you the correct name:
<spring:bind path="credentials.pwdcreds.pwdcreds">
<input type="password" name="${status.expression}" value="${status.value}"/>
</spring:bind>
Try that.
pearl8
Feb 11th, 2006, 07:31 AM
wonderful. it worked! yatesco, endlesswinter, i must say that as much as spring is a great framework, it's also wonderful great developers like you that help the newbies like me help me get on my feet. your patience all through is much appreciated, and thank you so much for the spirit!
Colin Yates
Feb 11th, 2006, 04:21 PM
it's also wonderful great developers like you that help the newbies like me help me get on my feet
Hey, today a newbie; tomorrow a "great developer" (in your words) :)
Seriously; everybody has something to contribute.
vBulletin® v3.7.3, Copyright ©2000-2008, Jelsoft Enterprises Ltd.