I will commonly have code that looks like:
obj.setFoo(value);
if(object.validate());
{
save(obj);
}
return obj;
Then if the object doesn't validate, I can pass it back to my view,
and it can display the invalid data just as it would do normally.
That make sense?
Mark
That is what I would usually do, but I'm guessing from his email that
he's storing the user object in the session scope and is worried that
if they don't fix their user data up, the unvalidated, unsaved data
will now show on all other pages.
For example, you change your last name to
Chumbawumbachumbawumbachumbawumbachumbawumba but last name has a 20
character max length. If he does session.User.save(LastName ,
form.LastName), the session.User.isValid() will return false, but the
session.User.getLastName() used to welcome you to every page as hello
FirstName LastName will show the invalid version for the rest of the
session.
Michael - was that the concern you had? If so I'd consider instead of
editing (what I call) the SiteUser, just load and edit a User object
and then you can session.SiteUser =
UserService.getByID( session.SiteUser.getID() ); to reload any changes
to the site user in session scope once the new User object has been
successfully validated and saved.
Would that work?
Best Wishes,
Peter
That way if it's invalid, you pass that back, otherwise, switch it out
in the session scope, or has a way to copy values b/w the objects.
Mark
If I have a date field in my bean, then I would like it to be a real date
value (or an empty string meaning null), or if I have a numeric field in my
bean then I want it to be a real numeric value. Allowing them to be anything
entered by a user just doesn't feel right - but that is just where my head
is at right now.
I have been handling general add/edit forms as follows (it's a bit of a long
story ...)
I create a "FormHelper" object that is aware of all fields on the form
(including hidden, visible and dynamic fields). It knows how to transform
database data or objects into form field values and visa versa.
For a User add/edit form I would have a UserFormHelper with some functions
as follows:
* setField(name,value)
* getField(name)
Functions to set/get form field values.
* initialiseFields()
This initialises an internal struct with all of the fields in the form. It
sets each of the fields to their initial value. These values would be for
"add/new" forms. This function essentially defines the fields that are
permitted in the form. The field names exactly match the actual form field
names. This is called from the init() function and has code that looks like:
<cfset setField("userId",0)>
<cfset setField("firstName","")>
<cfset setField("lastName","")>
* loadByUsedId(userId)
This initialises the helper fields from the database. But this could also be
something like loadByUserBean(user). This calls the setField() function for
each field, but with the value from the database/bean. This is used for an
"edit" form.
* loadByEvent(event)
This initialises the helper fields from an "event", which is just the
combined form and url scopes. This is used when attempting to save a form.
The submitted form values are passed into the helper via this function.
* validate()
This validates the form data. This may also populate beans and call their
validate() functions, but this helper validation is aware of the entire form
which may span multiple bean types. This function sets errors inside the
helper.
* save()
Calls validate() and if no errors, performs the save (via
services/gateways). If errors during the save occur, then additional errors
may be set.
I would generally use the helper as follows:
* newUser event:
<cfset helper = createObject("component","UserFormHelper").init()>
In the form, use code such as:
<cfset userId = helper.getField("userId")>
<input type="hidden" name="userId" value="#userId#">
<cfset firstName = helper.getField("firstName")>
<input type="hidden" name="firstName" value="#userId#">
* editUser event:
<cfset userId = event.get("userId")>
<cfset helper = createObject("component","UserFormHelper").init()>
<cfset helper.loadByUserId(userId)>
Then the form display code is the same as above.
* saveUser event:
<cfset helper = createObject("component","UserFormHelper").init()>
<cfset helper.loadByEvent(event)>
<cfset helper.save()>
<cfif not helper.hasErrors()>
<cfset userId = helper.getField("userId")>
<cflocation
url="/some/dir/index.cfm?event=editUser&userId=#userId#">
</cfif>
<!---
Then display the same form again, but with error messages. The helper has a
function getErrors() which returns a collection of formfield error messages.
--->
The helper manages the data with the form display in mind. It holds any
submitted invalid data which is redisplayed exactly as entered back onto the
form when errors occur, it also prepares data for display - such as
converting datetime fields from the database into the correct display values
for the form. Essentially it is a representation of the form as the server
sees it - without the UI - just a bunch of name/value pairs.
I have a base FormHelper that all other form helpers extend, so there is not
much work in setting up new forms.
Overall this has been working out quite nicely for me, but would love to
hear others ideas.
Kevan
I would see the flow as (or something similar) -
- put your current session.oUser in the request scope.
- Form loads with <input type="text" name="email" id="email"
value="#request.oUser.getEmail()#" />
- on submit duplicate() the session.oUser and put it in request scope
- load in the form data into the request scope's object (the copy)
- if validation fails, re-route back to the display page
- page still loads with <input type="text" name="email" id="email"
value="#request.oUser.getEmail()#" />
- session is still intact.
Make sense?
Hi Brian, Mark
If a form field contains invalid data that cannot be set on a bean due to a type failure, how would you handle redisplaying that original invalid data back on the form again?
Thanks
Kevan
So, my solution works, allows me to use my business object directly in
my view (e.g., myObj.getName()), and also allows me to redisplay any
invalid values entered by a user, but I'm just not that keen on the
implementation. I hope to one day either hear of a better
implementation or come up with something on my own.
On Fri, Sep 26, 2008 at 4:51 PM, Kevan Stannard <ke...@stannard.net.au> wrote:
--
Bob Silverberg
www.silverwareconsulting.com
Keep business object exactly as it is, with typed getters and setters.
Include the "shadow property" logic to store invalid data that cannot
be stored by a setter. As the business object is created by a
business object factory, wrap the business object in a generic
wrapper. That wrapper would use onMM to pass all setters to the
underlying business object, and to allow all getters to first check
for a shadow property and if one does not exist, then pass the getter
to the underlying business object.
This would allow one to keep the API of the business object, without
having to resort to generic getters and setters, but would remove the
previous requirement (in my implementation) of having to write a
custom getter for any properties that need to be able to store invalid
data. You could then use your business object directly in your view
and would always get back the data that the user submitted, valid or
not.
I haven't actually written this yet (I will), but in theory it sounds
pretty good.
Comments?
--
Bob Silverberg
www.silverwareconsulting.com
Just to confirm, you clone a copy of the transfer object and then
attempt to set each of the properties with a <cftry><cfcatch> around
it so if any of the properties fails to set you note it, and if no
errors were detected you make a second pass at each property for the
additional rules? If any errors were detected on either pass you
return the cloned object to the form to re-populate the form. If no
errors were detected, save the cloned transfer object, if saved
successfully, update your original object from the clone?
Sorry if that is a little incoherent, but I want to make sure I am
following you from form submission and back to the form if errors
occur, and if not, persisting to the DB. I have read the other posts
in the group here regarding changing the type to "Any" or not and I
think that is what you were addressing here. Also the issue of making
sure the session scope object in in sync as to what is in the DB.
Thanks,
Dan
Was wondering if you gave this idea a spin and have prototyped it -
and if so, did it work as expected for you.
Thanks,
Dan
Bob
--
Bob Silverberg
www.silverwareconsulting.com
Could not agree more. I am all for usability but at some point, have
to put some ownership on the user to have a clue. Thanks for the
verification. I am going to model out a test case and try it.
Dan
data entry operators who can fly through dozens of form elements using
taborder, <enter> and shortcut keys to do so.
you want horror stories of 50-70 form fields on one screen,
micro-business logic modifying the taborder and/or disabling form
elements and/or kicking in custom validation?
where entering "12102008" in a text box will automatically render it
as "12/10/2008" as a date in October?
thank small miracles for CF custom tags as tag libs to reuse controls.
Pity it all happened pre Flex2.
We also had a client who requested an application to function without JS
enabled. It was not practical in that particular case, but I can see that
these scenarios do come up.
-----Original Message-----
From: cfc...@googlegroups.com [mailto:cfc...@googlegroups.com] On Behalf Of
Barry Beattie
Sent: Tuesday, 14 October 2008 6:21 PM
To: cfc...@googlegroups.com
Perfect solution, but that was just one example of not repopulating a
field that fails the property type set.
@Jean - good point - but not big enough of an issue for me to worry
about now. Maybe as I flush out my process I will find a solution for
it. But still, if I prompt them for age and they enter aa, and enter
aa again, and they have JS turned off, I am OK with passing on that
user.
Dan