You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Christoph Jäger <ch...@derwald.at> on 2008/04/22 22:54:37 UTC

T5: Forms - Best Practice

Hi,

Sorry for this long post. I spent quite some time now to try to figure  
out how to use forms to edit/update objects the "right" way (you know,  
simple, stable, elegant, easy to read, easy add things, ...). I use  
Tapestry 5.0.11. I am almost satisfied with what I came up with now,  
but some improvements need to be done. Maybe someone on this list can  
help with some ideas.

As an example, lets have a PersonEdit page, with a PersonDao injected.  
You can create new Person entries or edit existing ones. To edit an  
existing person, there is a PageLink on a PersonSearch page, which has  
the Person's primary id as context. To create a new Person, a PageLink  
with 0 as context is used. To make this work, we have onActivate() and  
onPassivate() methods in PersonEdit:

void onActivate(int id)
{
   _id = id;
   if (id == 0)
   {
     _person = new Person();
   }
   else
   {
     _person=personDao.get(id);
   }
}

int onPassivate()
{
   return _id;
}

This way we can avoid using @Persist on the Person property (because,  
for instance, we want a user to be able to open two browser windows,  
viewing two different Person entries side by side and edit and save  
both of them. I think this would be problematic if we use @Persist,  
but please correct me if I am wrong).

Now, editing an existing user works like this:

- click the "edit user XYZ" PageLink on the PersonSearch page
- in onActivate(), the personDao is used to query the Person from the  
database
- an HTML form is rendered to let the user edit the values

Up to here everything looks perfect.

- the user edits the Person's data and hits the "save" button

- onActivate() is called, a fresh Person is loaded from the database
- for each field in the HTML form, validation is done (if defined),  
and a property in the fresh Person instance is set if the validation  
was successful
- onValidateForm() is called if existing to allow for cross-field  
validation
- if all validations were successful, onSuccess() is called. Here we  
call _person=personDao.save(_person). This save method returns a new  
Person instance, exactly as it was written to the database (primary id  
may be generated by the database, time stamps or version numbers  
updated, ...). We use this new Person's id : _id=_person.getId() to  
make sure we have the correct if for the next onPassivate()
- onPassivate() is called
- result sent to browser, redirect
- onActivate() loads Person again
- new form is rendered

This is good, but I think it could be improved.

1. The Person is loaded from the database twice (using  
personDao.get()), and saved once. The save() method of personDao  
already gives us a new, fresh instance of Person, it seems like a  
waste to load it again after the redirect.

2. During validation, we check if there is already a Person with the  
same userid (in onValidateForm(), or in onValidateFromUserId()) and  
warn the user if this is the case. But what happens if a new Person  
with this userId is added just after onValidateForm() is called, but  
before onSuccess() is called, where we want to save? To make our  
program solid, we have to take this into account. In case save() does  
not work, we do not want to see some exception page, but the form, as  
it was filled, with a hint what might have gone wrong, so the user can  
try again. To make this possible, we have to move the save() to  
onValidateForm(), because there we can still record form errors (I  
think there is a JIRA for an improvement to this situation).

3. We want to give the user feedback of what happened. After clicking  
the save button, we want to show a message like "Successfully saved  
new person information" above the form (at the same place you would  
see error messages like: "The first name is mandatory"). The only  
thing that comes to my mind is to use @Persist to save the message  
from the onSuccess() or onValidateForm() through the redirect to the  
next page render. But now I have problems to make sure this  
@Persist'ed message is cleared and is only presented to the user  
directly after the save button was clicked.


I think to find a nice solution for these issues (you know, easy  
things should be easy to do, and a form like this does not sound very  
complex to me, so there should be an easy solution), a @Persist, which  
persists values just through the redirect would be very helpful. The  
problem is, I have no idea how this could work. But maybe an option  
like this already exists (I am sure I am not the first to have  
problems like this), and I just didn't find it.

Any ideas are welcome,

Thanks,

Christoph Jäger



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


RE: T5: Forms - Best Practice

Posted by Jonathan Barker <jo...@gmail.com>.
Jim,

I have a T4 application that uses domain-object level security implemented
using the Acegi framework.  I have used custom authorization code more
appropriate for my task, rather than the built-in Acegi method.

It works beautifully, and I've certainly seen people attempting to access
things they shouldn't.

Jonathan
 

> -----Original Message-----
> From: Jim [mailto:openbip@gmail.com]
> Sent: Tuesday, April 22, 2008 10:08 PM
> To: Tapestry users
> Subject: Re: T5: Forms - Best Practice
> 
> Hi Christoph,
> 
> I know you're mostly asking about best-practice of process rather than
> security, but I think it's nonetheless important to bring up the issues
> that can arise when embedding primary keys of your entities client-side.
> 
> I'm still a T4 user, but when you say "a PageLink on a PersonSearch
> page, which has the Person's primary id as context", I assume that's
> analogous to the "parameters" attribute on a DirectLink in T4, in that
> the parameter is embedded in the page.  Even if that's not the case,
> I'll continue anyway :P
> 
> Building on your example, let's say that the logged-in user should only
> be able to search/edit people within his/her department.  If we're only
> passing the PersonID to the PersonEdit page, and that ID is embedded in
> each rendered link on the PersonSearch page output, then the user could
> hack the form/link from the PersonSearch page to pass an arbitrary
> PersonID to PersonEdit, potentially giving the user the ability to
> perform unauthorized edits.
> 
> I've started taking the approach of embedding not the ID, but a piece of
> information that's unique within the security-context (by
> "security-context", in this case I mean "department").  In this case,
> assuming that a person's full name is unique within a department, we
> could embed the person's full name into the PageLink, and the PersonEdit
> page would search not on the primary key of the person, but instead on
> the combination of the DepartmentID (retrieved from the session if we're
> keeping a User object in the session) and the full name we passed in.
> Since we're using the DepartmentID from the session, then the user can
> hack the form/link all he/she wants, and the best they'll do is bring up
> the editing form for someone that wasn't in the search results but is
> still in their department, so it'd still be an authorized action.
> 
> This sort of approach is annoying, because we'd love to be cleanly using
> solid efficient primary keys, but it probably pays to be paranoid.
> Anyone have a better approach on this issue?  Particularly with regard
> to search pages?
> 
> Jim
> 
> 
> Christoph Jäger wrote:
> > Hi,
> >
> > Sorry for this long post. I spent quite some time now to try to figure
> > out how to use forms to edit/update objects the "right" way (you know,
> > simple, stable, elegant, easy to read, easy add things, ...). I use
> > Tapestry 5.0.11. I am almost satisfied with what I came up with now,
> > but some improvements need to be done. Maybe someone on this list can
> > help with some ideas.
> >
> > As an example, lets have a PersonEdit page, with a PersonDao injected.
> > You can create new Person entries or edit existing ones. To edit an
> > existing person, there is a PageLink on a PersonSearch page, which has
> > the Person's primary id as context. To create a new Person, a PageLink
> > with 0 as context is used. To make this work, we have onActivate() and
> > onPassivate() methods in PersonEdit:
> >
> > void onActivate(int id)
> > {
> >   _id = id;
> >   if (id == 0)
> >   {
> >     _person = new Person();
> >   }
> >   else
> >   {
> >     _person=personDao.get(id);
> >   }
> > }
> >
> > int onPassivate()
> > {
> >   return _id;
> > }
> >
> > This way we can avoid using @Persist on the Person property (because,
> > for instance, we want a user to be able to open two browser windows,
> > viewing two different Person entries side by side and edit and save
> > both of them. I think this would be problematic if we use @Persist,
> > but please correct me if I am wrong).
> >
> > Now, editing an existing user works like this:
> >
> > - click the "edit user XYZ" PageLink on the PersonSearch page
> > - in onActivate(), the personDao is used to query the Person from the
> > database
> > - an HTML form is rendered to let the user edit the values
> >
> > Up to here everything looks perfect.
> >
> > - the user edits the Person's data and hits the "save" button
> >
> > - onActivate() is called, a fresh Person is loaded from the database
> > - for each field in the HTML form, validation is done (if defined),
> > and a property in the fresh Person instance is set if the validation
> > was successful
> > - onValidateForm() is called if existing to allow for cross-field
> > validation
> > - if all validations were successful, onSuccess() is called. Here we
> > call _person=personDao.save(_person). This save method returns a new
> > Person instance, exactly as it was written to the database (primary id
> > may be generated by the database, time stamps or version numbers
> > updated, ...). We use this new Person's id : _id=_person.getId() to
> > make sure we have the correct if for the next onPassivate()
> > - onPassivate() is called
> > - result sent to browser, redirect
> > - onActivate() loads Person again
> > - new form is rendered
> >
> > This is good, but I think it could be improved.
> >
> > 1. The Person is loaded from the database twice (using
> > personDao.get()), and saved once. The save() method of personDao
> > already gives us a new, fresh instance of Person, it seems like a
> > waste to load it again after the redirect.
> >
> > 2. During validation, we check if there is already a Person with the
> > same userid (in onValidateForm(), or in onValidateFromUserId()) and
> > warn the user if this is the case. But what happens if a new Person
> > with this userId is added just after onValidateForm() is called, but
> > before onSuccess() is called, where we want to save? To make our
> > program solid, we have to take this into account. In case save() does
> > not work, we do not want to see some exception page, but the form, as
> > it was filled, with a hint what might have gone wrong, so the user can
> > try again. To make this possible, we have to move the save() to
> > onValidateForm(), because there we can still record form errors (I
> > think there is a JIRA for an improvement to this situation).
> >
> > 3. We want to give the user feedback of what happened. After clicking
> > the save button, we want to show a message like "Successfully saved
> > new person information" above the form (at the same place you would
> > see error messages like: "The first name is mandatory"). The only
> > thing that comes to my mind is to use @Persist to save the message
> > from the onSuccess() or onValidateForm() through the redirect to the
> > next page render. But now I have problems to make sure this
> > @Persist'ed message is cleared and is only presented to the user
> > directly after the save button was clicked.
> >
> >
> > I think to find a nice solution for these issues (you know, easy
> > things should be easy to do, and a form like this does not sound very
> > complex to me, so there should be an easy solution), a @Persist, which
> > persists values just through the redirect would be very helpful. The
> > problem is, I have no idea how this could work. But maybe an option
> > like this already exists (I am sure I am not the first to have
> > problems like this), and I just didn't find it.
> >
> > Any ideas are welcome,
> >
> > Thanks,
> >
> > Christoph Jäger
> >
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> > For additional commands, e-mail: users-help@tapestry.apache.org
> >
> >
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: T5: Forms - Best Practice

Posted by César Lesc <ce...@gmail.com>.
Personally i never used but may be you can use the UUID (
http://en.wikipedia.org/wiki/Universally_Unique_Identifier ) for yours
employees primary key or alternate key. They are very hard to guess.

and if you get very paranoid you can encrypt them.

César.

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: T5: Forms - Best Practice

Posted by Christoph Jäger <ch...@derwald.at>.
Hi Jim,

I like to use the primary key, because it is short and by definition  
unique, and usually the quickest way to retrieve an object from the  
database.

For the problem you describe, users are only allowed to view people in  
their own department, three different solutions come to my mind:

1. in onActivate(), where the personDao is queried for the Person with  
the given id, check if the logged in user may view the requested  
Person information
2. in the PersonDao itself check for permissions
3. as Jonathan Barker wrote in an email in this thread, use domain- 
object level security

1 is the quick and dirty way, easy to leave some holes open, but it  
should work in any environment. 2 only works if personDao (wherever it  
is executed, might be a remote call on some different server than your  
web-app) knows about the logged in user, and for 3 you probably have  
to use some framework like Acegi or some container managed  
authentication and authorization.

But there is a different scenario, where we have to find a different  
solution: imagine some web-frontend to a database of companies.  
Everyone can search for companies, but you want to limit the search to  
show only the top 10 results, so no one can harvest your whole  
database. Using the primary key here proves to be easily exploitable,  
just try the result page with ids from 1 to 10000 (it is quite easy to  
use wget and some lines of perl code to extract the whole database  
this way). You can try to limit the rate of requests from a single IP  
address and stuff like this, but this only slows down things. And you  
can not use security settings here, because everyone is meant to be  
able to view each entry (only not all of them). I did not yet have to  
solve a problem like this (only discussed it). You could probably use  
some alternate keys, created as hash values from your primary key and  
some "secret" string, or use UUIDs like Cesar Lesc mentioned in his  
post on this thread.

Best Regards,

Christoph

On Apr 23, 2008, at 04:08 , Jim wrote:

> Hi Christoph,
>
> I know you're mostly asking about best-practice of process rather  
> than security, but I think it's nonetheless important to bring up  
> the issues that can arise when embedding primary keys of your  
> entities client-side.
>
> I'm still a T4 user, but when you say "a PageLink on a PersonSearch  
> page, which has the Person's primary id as context", I assume that's  
> analogous to the "parameters" attribute on a DirectLink in T4, in  
> that the parameter is embedded in the page.  Even if that's not the  
> case, I'll continue anyway :P
>
> Building on your example, let's say that the logged-in user should  
> only be able to search/edit people within his/her department.  If  
> we're only passing the PersonID to the PersonEdit page, and that ID  
> is embedded in each rendered link on the PersonSearch page output,  
> then the user could hack the form/link from the PersonSearch page to  
> pass an arbitrary PersonID to PersonEdit, potentially giving the  
> user the ability to perform unauthorized edits.
>
> I've started taking the approach of embedding not the ID, but a  
> piece of information that's unique within the security-context (by  
> "security-context", in this case I mean "department").  In this  
> case, assuming that a person's full name is unique within a  
> department, we could embed the person's full name into the PageLink,  
> and the PersonEdit page would search not on the primary key of the  
> person, but instead on the combination of the DepartmentID  
> (retrieved from the session if we're keeping a User object in the  
> session) and the full name we passed in.  Since we're using the  
> DepartmentID from the session, then the user can hack the form/link  
> all he/she wants, and the best they'll do is bring up the editing  
> form for someone that wasn't in the search results but is still in  
> their department, so it'd still be an authorized action.
>
> This sort of approach is annoying, because we'd love to be cleanly  
> using solid efficient primary keys, but it probably pays to be  
> paranoid.  Anyone have a better approach on this issue?   
> Particularly with regard to search pages?
>
> Jim
>
>
> Christoph Jäger wrote:
>> Hi,
>>
>> Sorry for this long post. I spent quite some time now to try to  
>> figure out how to use forms to edit/update objects the "right" way  
>> (you know, simple, stable, elegant, easy to read, easy add  
>> things, ...). I use Tapestry 5.0.11. I am almost satisfied with  
>> what I came up with now, but some improvements need to be done.  
>> Maybe someone on this list can help with some ideas.
>>
>> As an example, lets have a PersonEdit page, with a PersonDao  
>> injected. You can create new Person entries or edit existing ones.  
>> To edit an existing person, there is a PageLink on a PersonSearch  
>> page, which has the Person's primary id as context. To create a new  
>> Person, a PageLink with 0 as context is used. To make this work, we  
>> have onActivate() and onPassivate() methods in PersonEdit:
>>
>> void onActivate(int id)
>> {
>>  _id = id;
>>  if (id == 0)
>>  {
>>    _person = new Person();
>>  }
>>  else
>>  {
>>    _person=personDao.get(id);
>>  }
>> }
>>
>> int onPassivate()
>> {
>>  return _id;
>> }
>>
>> This way we can avoid using @Persist on the Person property  
>> (because, for instance, we want a user to be able to open two  
>> browser windows, viewing two different Person entries side by side  
>> and edit and save both of them. I think this would be problematic  
>> if we use @Persist, but please correct me if I am wrong).
>>
>> Now, editing an existing user works like this:
>>
>> - click the "edit user XYZ" PageLink on the PersonSearch page
>> - in onActivate(), the personDao is used to query the Person from  
>> the database
>> - an HTML form is rendered to let the user edit the values
>>
>> Up to here everything looks perfect.
>>
>> - the user edits the Person's data and hits the "save" button
>>
>> - onActivate() is called, a fresh Person is loaded from the database
>> - for each field in the HTML form, validation is done (if defined),  
>> and a property in the fresh Person instance is set if the  
>> validation was successful
>> - onValidateForm() is called if existing to allow for cross-field  
>> validation
>> - if all validations were successful, onSuccess() is called. Here  
>> we call _person=personDao.save(_person). This save method returns a  
>> new Person instance, exactly as it was written to the database  
>> (primary id may be generated by the database, time stamps or  
>> version numbers updated, ...). We use this new Person's id :  
>> _id=_person.getId() to make sure we have the correct if for the  
>> next onPassivate()
>> - onPassivate() is called
>> - result sent to browser, redirect
>> - onActivate() loads Person again
>> - new form is rendered
>>
>> This is good, but I think it could be improved.
>>
>> 1. The Person is loaded from the database twice (using  
>> personDao.get()), and saved once. The save() method of personDao  
>> already gives us a new, fresh instance of Person, it seems like a  
>> waste to load it again after the redirect.
>>
>> 2. During validation, we check if there is already a Person with  
>> the same userid (in onValidateForm(), or in onValidateFromUserId())  
>> and warn the user if this is the case. But what happens if a new  
>> Person with this userId is added just after onValidateForm() is  
>> called, but before onSuccess() is called, where we want to save? To  
>> make our program solid, we have to take this into account. In case  
>> save() does not work, we do not want to see some exception page,  
>> but the form, as it was filled, with a hint what might have gone  
>> wrong, so the user can try again. To make this possible, we have to  
>> move the save() to onValidateForm(), because there we can still  
>> record form errors (I think there is a JIRA for an improvement to  
>> this situation).
>>
>> 3. We want to give the user feedback of what happened. After  
>> clicking the save button, we want to show a message like  
>> "Successfully saved new person information" above the form (at the  
>> same place you would see error messages like: "The first name is  
>> mandatory"). The only thing that comes to my mind is to use  
>> @Persist to save the message from the onSuccess() or  
>> onValidateForm() through the redirect to the next page render. But  
>> now I have problems to make sure this @Persist'ed message is  
>> cleared and is only presented to the user directly after the save  
>> button was clicked.
>>
>>
>> I think to find a nice solution for these issues (you know, easy  
>> things should be easy to do, and a form like this does not sound  
>> very complex to me, so there should be an easy solution), a  
>> @Persist, which persists values just through the redirect would be  
>> very helpful. The problem is, I have no idea how this could work.  
>> But maybe an option like this already exists (I am sure I am not  
>> the first to have problems like this), and I just didn't find it.
>>
>> Any ideas are welcome,
>>
>> Thanks,
>>
>> Christoph Jäger
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>

Christoph Jäger
christoph.jaeger@derwald.at





---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: T5: Forms - Best Practice

Posted by Jim <op...@gmail.com>.
Hi Christoph,

I know you're mostly asking about best-practice of process rather than 
security, but I think it's nonetheless important to bring up the issues 
that can arise when embedding primary keys of your entities client-side.

I'm still a T4 user, but when you say "a PageLink on a PersonSearch 
page, which has the Person's primary id as context", I assume that's 
analogous to the "parameters" attribute on a DirectLink in T4, in that 
the parameter is embedded in the page.  Even if that's not the case, 
I'll continue anyway :P

Building on your example, let's say that the logged-in user should only 
be able to search/edit people within his/her department.  If we're only 
passing the PersonID to the PersonEdit page, and that ID is embedded in 
each rendered link on the PersonSearch page output, then the user could 
hack the form/link from the PersonSearch page to pass an arbitrary 
PersonID to PersonEdit, potentially giving the user the ability to 
perform unauthorized edits.

I've started taking the approach of embedding not the ID, but a piece of 
information that's unique within the security-context (by 
"security-context", in this case I mean "department").  In this case, 
assuming that a person's full name is unique within a department, we 
could embed the person's full name into the PageLink, and the PersonEdit 
page would search not on the primary key of the person, but instead on 
the combination of the DepartmentID (retrieved from the session if we're 
keeping a User object in the session) and the full name we passed in.  
Since we're using the DepartmentID from the session, then the user can 
hack the form/link all he/she wants, and the best they'll do is bring up 
the editing form for someone that wasn't in the search results but is 
still in their department, so it'd still be an authorized action.

This sort of approach is annoying, because we'd love to be cleanly using 
solid efficient primary keys, but it probably pays to be paranoid.  
Anyone have a better approach on this issue?  Particularly with regard 
to search pages?

Jim


Christoph Jäger wrote:
> Hi,
>
> Sorry for this long post. I spent quite some time now to try to figure 
> out how to use forms to edit/update objects the "right" way (you know, 
> simple, stable, elegant, easy to read, easy add things, ...). I use 
> Tapestry 5.0.11. I am almost satisfied with what I came up with now, 
> but some improvements need to be done. Maybe someone on this list can 
> help with some ideas.
>
> As an example, lets have a PersonEdit page, with a PersonDao injected. 
> You can create new Person entries or edit existing ones. To edit an 
> existing person, there is a PageLink on a PersonSearch page, which has 
> the Person's primary id as context. To create a new Person, a PageLink 
> with 0 as context is used. To make this work, we have onActivate() and 
> onPassivate() methods in PersonEdit:
>
> void onActivate(int id)
> {
>   _id = id;
>   if (id == 0)
>   {
>     _person = new Person();
>   }
>   else
>   {
>     _person=personDao.get(id);
>   }
> }
>
> int onPassivate()
> {
>   return _id;
> }
>
> This way we can avoid using @Persist on the Person property (because, 
> for instance, we want a user to be able to open two browser windows, 
> viewing two different Person entries side by side and edit and save 
> both of them. I think this would be problematic if we use @Persist, 
> but please correct me if I am wrong).
>
> Now, editing an existing user works like this:
>
> - click the "edit user XYZ" PageLink on the PersonSearch page
> - in onActivate(), the personDao is used to query the Person from the 
> database
> - an HTML form is rendered to let the user edit the values
>
> Up to here everything looks perfect.
>
> - the user edits the Person's data and hits the "save" button
>
> - onActivate() is called, a fresh Person is loaded from the database
> - for each field in the HTML form, validation is done (if defined), 
> and a property in the fresh Person instance is set if the validation 
> was successful
> - onValidateForm() is called if existing to allow for cross-field 
> validation
> - if all validations were successful, onSuccess() is called. Here we 
> call _person=personDao.save(_person). This save method returns a new 
> Person instance, exactly as it was written to the database (primary id 
> may be generated by the database, time stamps or version numbers 
> updated, ...). We use this new Person's id : _id=_person.getId() to 
> make sure we have the correct if for the next onPassivate()
> - onPassivate() is called
> - result sent to browser, redirect
> - onActivate() loads Person again
> - new form is rendered
>
> This is good, but I think it could be improved.
>
> 1. The Person is loaded from the database twice (using 
> personDao.get()), and saved once. The save() method of personDao 
> already gives us a new, fresh instance of Person, it seems like a 
> waste to load it again after the redirect.
>
> 2. During validation, we check if there is already a Person with the 
> same userid (in onValidateForm(), or in onValidateFromUserId()) and 
> warn the user if this is the case. But what happens if a new Person 
> with this userId is added just after onValidateForm() is called, but 
> before onSuccess() is called, where we want to save? To make our 
> program solid, we have to take this into account. In case save() does 
> not work, we do not want to see some exception page, but the form, as 
> it was filled, with a hint what might have gone wrong, so the user can 
> try again. To make this possible, we have to move the save() to 
> onValidateForm(), because there we can still record form errors (I 
> think there is a JIRA for an improvement to this situation).
>
> 3. We want to give the user feedback of what happened. After clicking 
> the save button, we want to show a message like "Successfully saved 
> new person information" above the form (at the same place you would 
> see error messages like: "The first name is mandatory"). The only 
> thing that comes to my mind is to use @Persist to save the message 
> from the onSuccess() or onValidateForm() through the redirect to the 
> next page render. But now I have problems to make sure this 
> @Persist'ed message is cleared and is only presented to the user 
> directly after the save button was clicked.
>
>
> I think to find a nice solution for these issues (you know, easy 
> things should be easy to do, and a form like this does not sound very 
> complex to me, so there should be an easy solution), a @Persist, which 
> persists values just through the redirect would be very helpful. The 
> problem is, I have no idea how this could work. But maybe an option 
> like this already exists (I am sure I am not the first to have 
> problems like this), and I just didn't find it.
>
> Any ideas are welcome,
>
> Thanks,
>
> Christoph Jäger
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: T5: Forms - Best Practice

Posted by nicholas Krul <ni...@gmail.com>.
@Persist("flash")
http://tapestry.apache.org/tapestry5/tapestry-core/guide/persist.html

and, since I can peddle my own wares
@Persist("flashcookie")
http://wiki.apache.org/tapestry/Tapestry5HowToUsePersistentFieldStrategy

I've had one (or two) too many glasses of wine. May your night be as good as
mine  :)

--nK

On Tue, Apr 22, 2008 at 9:54 PM, Christoph Jäger <
christoph.jaeger@derwald.at> wrote:

> Hi,
>
> Sorry for this long post. I spent quite some time now to try to figure out
> how to use forms to edit/update objects the "right" way (you know, simple,
> stable, elegant, easy to read, easy add things, ...). I use Tapestry
> 5.0.11. I am almost satisfied with what I came up with now, but some
> improvements need to be done. Maybe someone on this list can help with some
> ideas.
>
> As an example, lets have a PersonEdit page, with a PersonDao injected. You
> can create new Person entries or edit existing ones. To edit an existing
> person, there is a PageLink on a PersonSearch page, which has the Person's
> primary id as context. To create a new Person, a PageLink with 0 as context
> is used. To make this work, we have onActivate() and onPassivate() methods
> in PersonEdit:
>
> void onActivate(int id)
> {
>  _id = id;
>  if (id == 0)
>  {
>    _person = new Person();
>  }
>  else
>  {
>    _person=personDao.get(id);
>  }
> }
>
> int onPassivate()
> {
>  return _id;
> }
>
> This way we can avoid using @Persist on the Person property (because, for
> instance, we want a user to be able to open two browser windows, viewing two
> different Person entries side by side and edit and save both of them. I
> think this would be problematic if we use @Persist, but please correct me if
> I am wrong).
>
> Now, editing an existing user works like this:
>
> - click the "edit user XYZ" PageLink on the PersonSearch page
> - in onActivate(), the personDao is used to query the Person from the
> database
> - an HTML form is rendered to let the user edit the values
>
> Up to here everything looks perfect.
>
> - the user edits the Person's data and hits the "save" button
>
> - onActivate() is called, a fresh Person is loaded from the database
> - for each field in the HTML form, validation is done (if defined), and a
> property in the fresh Person instance is set if the validation was
> successful
> - onValidateForm() is called if existing to allow for cross-field
> validation
> - if all validations were successful, onSuccess() is called. Here we call
> _person=personDao.save(_person). This save method returns a new Person
> instance, exactly as it was written to the database (primary id may be
> generated by the database, time stamps or version numbers updated, ...). We
> use this new Person's id : _id=_person.getId() to make sure we have the
> correct if for the next onPassivate()
> - onPassivate() is called
> - result sent to browser, redirect
> - onActivate() loads Person again
> - new form is rendered
>
> This is good, but I think it could be improved.
>
> 1. The Person is loaded from the database twice (using personDao.get()),
> and saved once. The save() method of personDao already gives us a new, fresh
> instance of Person, it seems like a waste to load it again after the
> redirect.
>
> 2. During validation, we check if there is already a Person with the same
> userid (in onValidateForm(), or in onValidateFromUserId()) and warn the user
> if this is the case. But what happens if a new Person with this userId is
> added just after onValidateForm() is called, but before onSuccess() is
> called, where we want to save? To make our program solid, we have to take
> this into account. In case save() does not work, we do not want to see some
> exception page, but the form, as it was filled, with a hint what might have
> gone wrong, so the user can try again. To make this possible, we have to
> move the save() to onValidateForm(), because there we can still record form
> errors (I think there is a JIRA for an improvement to this situation).
>
> 3. We want to give the user feedback of what happened. After clicking the
> save button, we want to show a message like "Successfully saved new person
> information" above the form (at the same place you would see error messages
> like: "The first name is mandatory"). The only thing that comes to my mind
> is to use @Persist to save the message from the onSuccess() or
> onValidateForm() through the redirect to the next page render. But now I
> have problems to make sure this @Persist'ed message is cleared and is only
> presented to the user directly after the save button was clicked.
>
>
> I think to find a nice solution for these issues (you know, easy things
> should be easy to do, and a form like this does not sound very complex to
> me, so there should be an easy solution), a @Persist, which persists values
> just through the redirect would be very helpful. The problem is, I have no
> idea how this could work. But maybe an option like this already exists (I am
> sure I am not the first to have problems like this), and I just didn't find
> it.
>
> Any ideas are welcome,
>
> Thanks,
>
> Christoph Jäger
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>

Re: T5: Forms - Best Practice

Posted by Geoff Callender <ge...@gmail.com>.
I know Howard appreciates the need for conversations, but he's pretty  
flat-out.
Actually, I'm not sure if there's a JIRA ticket for it.

On 24/04/2008, at 7:03 AM, Christoph Jäger wrote:

> Hi Geoff,
>
> it seems your JIRA describes just what I would like to have. I  
> especially like the idea with the conversation id, because my next  
> thought would have been: what happens if the user edits two  
> different persons in two different windows of his web browser at the  
> same time (over a slow network, it might actually be not too  
> difficult to have the two requests for each page interleaved).
>
> Unfortunately, there does not seem to be a lot of interest in your  
> issue, no fix version, no assignee :(. I just voted for it, maybe  
> this helps (I know, the best thing would be to fix it myself, but  
> this seems to be a bit big for a first contribution).
>
> The conversation id would be a nice tool anyhow, this way you could  
> persist things in the session without having to worry about  
> different requests to the same page mix things up. Is there  
> something like a conversation id in Tapestry right now?
>
> Best Regards,
>
> Christoph
>
> On Apr 23, 2008, at 00:45 , Geoff Callender wrote:
>
>> Totally agree!  See https://issues.apache.org/jira/browse/TAPESTRY-2138
>>
>> Workaround is @Persist combined with nullifying the persisted  
>> object in cleanupRender().
>>
>> Geoff
>> http://files.doublenegative.com.au/jumpstart/
>>
>> On 23/04/2008, at 6:54 AM, Christoph Jäger wrote:
>>
>>> Hi,
>>>
>>> Sorry for this long post. I spent quite some time now to try to  
>>> figure out how to use forms to edit/update objects the "right" way  
>>> (you know, simple, stable, elegant, easy to read, easy add  
>>> things, ...). I use Tapestry 5.0.11. I am almost satisfied with  
>>> what I came up with now, but some improvements need to be done.  
>>> Maybe someone on this list can help with some ideas.
>>>
>>> As an example, lets have a PersonEdit page, with a PersonDao  
>>> injected. You can create new Person entries or edit existing ones.  
>>> To edit an existing person, there is a PageLink on a PersonSearch  
>>> page, which has the Person's primary id as context. To create a  
>>> new Person, a PageLink with 0 as context is used. To make this  
>>> work, we have onActivate() and onPassivate() methods in PersonEdit:
>>>
>>> void onActivate(int id)
>>> {
>>> _id = id;
>>> if (id == 0)
>>> {
>>>  _person = new Person();
>>> }
>>> else
>>> {
>>>  _person=personDao.get(id);
>>> }
>>> }
>>>
>>> int onPassivate()
>>> {
>>> return _id;
>>> }
>>>
>>> This way we can avoid using @Persist on the Person property  
>>> (because, for instance, we want a user to be able to open two  
>>> browser windows, viewing two different Person entries side by side  
>>> and edit and save both of them. I think this would be problematic  
>>> if we use @Persist, but please correct me if I am wrong).
>>>
>>> Now, editing an existing user works like this:
>>>
>>> - click the "edit user XYZ" PageLink on the PersonSearch page
>>> - in onActivate(), the personDao is used to query the Person from  
>>> the database
>>> - an HTML form is rendered to let the user edit the values
>>>
>>> Up to here everything looks perfect.
>>>
>>> - the user edits the Person's data and hits the "save" button
>>>
>>> - onActivate() is called, a fresh Person is loaded from the database
>>> - for each field in the HTML form, validation is done (if  
>>> defined), and a property in the fresh Person instance is set if  
>>> the validation was successful
>>> - onValidateForm() is called if existing to allow for cross-field  
>>> validation
>>> - if all validations were successful, onSuccess() is called. Here  
>>> we call _person=personDao.save(_person). This save method returns  
>>> a new Person instance, exactly as it was written to the database  
>>> (primary id may be generated by the database, time stamps or  
>>> version numbers updated, ...). We use this new Person's id :  
>>> _id=_person.getId() to make sure we have the correct if for the  
>>> next onPassivate()
>>> - onPassivate() is called
>>> - result sent to browser, redirect
>>> - onActivate() loads Person again
>>> - new form is rendered
>>>
>>> This is good, but I think it could be improved.
>>>
>>> 1. The Person is loaded from the database twice (using  
>>> personDao.get()), and saved once. The save() method of personDao  
>>> already gives us a new, fresh instance of Person, it seems like a  
>>> waste to load it again after the redirect.
>>>
>>> 2. During validation, we check if there is already a Person with  
>>> the same userid (in onValidateForm(), or in  
>>> onValidateFromUserId()) and warn the user if this is the case. But  
>>> what happens if a new Person with this userId is added just after  
>>> onValidateForm() is called, but before onSuccess() is called,  
>>> where we want to save? To make our program solid, we have to take  
>>> this into account. In case save() does not work, we do not want to  
>>> see some exception page, but the form, as it was filled, with a  
>>> hint what might have gone wrong, so the user can try again. To  
>>> make this possible, we have to move the save() to  
>>> onValidateForm(), because there we can still record form errors (I  
>>> think there is a JIRA for an improvement to this situation).
>>>
>>> 3. We want to give the user feedback of what happened. After  
>>> clicking the save button, we want to show a message like  
>>> "Successfully saved new person information" above the form (at the  
>>> same place you would see error messages like: "The first name is  
>>> mandatory"). The only thing that comes to my mind is to use  
>>> @Persist to save the message from the onSuccess() or  
>>> onValidateForm() through the redirect to the next page render. But  
>>> now I have problems to make sure this @Persist'ed message is  
>>> cleared and is only presented to the user directly after the save  
>>> button was clicked.
>>>
>>>
>>> I think to find a nice solution for these issues (you know, easy  
>>> things should be easy to do, and a form like this does not sound  
>>> very complex to me, so there should be an easy solution), a  
>>> @Persist, which persists values just through the redirect would be  
>>> very helpful. The problem is, I have no idea how this could work.  
>>> But maybe an option like this already exists (I am sure I am not  
>>> the first to have problems like this), and I just didn't find it.
>>>
>>> Any ideas are welcome,
>>>
>>> Thanks,
>>>
>>> Christoph Jäger
>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>
> Christoph Jäger
> christoph.jaeger@derwald.at
>
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: T5: Forms - Best Practice

Posted by Christoph Jäger <ch...@derwald.at>.
Hi Geoff,

it seems your JIRA describes just what I would like to have. I  
especially like the idea with the conversation id, because my next  
thought would have been: what happens if the user edits two different  
persons in two different windows of his web browser at the same time  
(over a slow network, it might actually be not too difficult to have  
the two requests for each page interleaved).

Unfortunately, there does not seem to be a lot of interest in your  
issue, no fix version, no assignee :(. I just voted for it, maybe this  
helps (I know, the best thing would be to fix it myself, but this  
seems to be a bit big for a first contribution).

The conversation id would be a nice tool anyhow, this way you could  
persist things in the session without having to worry about different  
requests to the same page mix things up. Is there something like a  
conversation id in Tapestry right now?

Best Regards,

Christoph

On Apr 23, 2008, at 00:45 , Geoff Callender wrote:

> Totally agree!  See https://issues.apache.org/jira/browse/ 
> TAPESTRY-2138
>
> Workaround is @Persist combined with nullifying the persisted object  
> in cleanupRender().
>
> Geoff
> http://files.doublenegative.com.au/jumpstart/
>
> On 23/04/2008, at 6:54 AM, Christoph Jäger wrote:
>
>> Hi,
>>
>> Sorry for this long post. I spent quite some time now to try to  
>> figure out how to use forms to edit/update objects the "right" way  
>> (you know, simple, stable, elegant, easy to read, easy add  
>> things, ...). I use Tapestry 5.0.11. I am almost satisfied with  
>> what I came up with now, but some improvements need to be done.  
>> Maybe someone on this list can help with some ideas.
>>
>> As an example, lets have a PersonEdit page, with a PersonDao  
>> injected. You can create new Person entries or edit existing ones.  
>> To edit an existing person, there is a PageLink on a PersonSearch  
>> page, which has the Person's primary id as context. To create a new  
>> Person, a PageLink with 0 as context is used. To make this work, we  
>> have onActivate() and onPassivate() methods in PersonEdit:
>>
>> void onActivate(int id)
>> {
>> _id = id;
>> if (id == 0)
>> {
>>   _person = new Person();
>> }
>> else
>> {
>>   _person=personDao.get(id);
>> }
>> }
>>
>> int onPassivate()
>> {
>> return _id;
>> }
>>
>> This way we can avoid using @Persist on the Person property  
>> (because, for instance, we want a user to be able to open two  
>> browser windows, viewing two different Person entries side by side  
>> and edit and save both of them. I think this would be problematic  
>> if we use @Persist, but please correct me if I am wrong).
>>
>> Now, editing an existing user works like this:
>>
>> - click the "edit user XYZ" PageLink on the PersonSearch page
>> - in onActivate(), the personDao is used to query the Person from  
>> the database
>> - an HTML form is rendered to let the user edit the values
>>
>> Up to here everything looks perfect.
>>
>> - the user edits the Person's data and hits the "save" button
>>
>> - onActivate() is called, a fresh Person is loaded from the database
>> - for each field in the HTML form, validation is done (if defined),  
>> and a property in the fresh Person instance is set if the  
>> validation was successful
>> - onValidateForm() is called if existing to allow for cross-field  
>> validation
>> - if all validations were successful, onSuccess() is called. Here  
>> we call _person=personDao.save(_person). This save method returns a  
>> new Person instance, exactly as it was written to the database  
>> (primary id may be generated by the database, time stamps or  
>> version numbers updated, ...). We use this new Person's id :  
>> _id=_person.getId() to make sure we have the correct if for the  
>> next onPassivate()
>> - onPassivate() is called
>> - result sent to browser, redirect
>> - onActivate() loads Person again
>> - new form is rendered
>>
>> This is good, but I think it could be improved.
>>
>> 1. The Person is loaded from the database twice (using  
>> personDao.get()), and saved once. The save() method of personDao  
>> already gives us a new, fresh instance of Person, it seems like a  
>> waste to load it again after the redirect.
>>
>> 2. During validation, we check if there is already a Person with  
>> the same userid (in onValidateForm(), or in onValidateFromUserId())  
>> and warn the user if this is the case. But what happens if a new  
>> Person with this userId is added just after onValidateForm() is  
>> called, but before onSuccess() is called, where we want to save? To  
>> make our program solid, we have to take this into account. In case  
>> save() does not work, we do not want to see some exception page,  
>> but the form, as it was filled, with a hint what might have gone  
>> wrong, so the user can try again. To make this possible, we have to  
>> move the save() to onValidateForm(), because there we can still  
>> record form errors (I think there is a JIRA for an improvement to  
>> this situation).
>>
>> 3. We want to give the user feedback of what happened. After  
>> clicking the save button, we want to show a message like  
>> "Successfully saved new person information" above the form (at the  
>> same place you would see error messages like: "The first name is  
>> mandatory"). The only thing that comes to my mind is to use  
>> @Persist to save the message from the onSuccess() or  
>> onValidateForm() through the redirect to the next page render. But  
>> now I have problems to make sure this @Persist'ed message is  
>> cleared and is only presented to the user directly after the save  
>> button was clicked.
>>
>>
>> I think to find a nice solution for these issues (you know, easy  
>> things should be easy to do, and a form like this does not sound  
>> very complex to me, so there should be an easy solution), a  
>> @Persist, which persists values just through the redirect would be  
>> very helpful. The problem is, I have no idea how this could work.  
>> But maybe an option like this already exists (I am sure I am not  
>> the first to have problems like this), and I just didn't find it.
>>
>> Any ideas are welcome,
>>
>> Thanks,
>>
>> Christoph Jäger
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>

Christoph Jäger
christoph.jaeger@derwald.at





---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: T5: Forms - Best Practice

Posted by Geoff Callender <ge...@gmail.com>.
Totally agree!  See https://issues.apache.org/jira/browse/TAPESTRY-2138

Workaround is @Persist combined with nullifying the persisted object  
in cleanupRender().

Geoff
http://files.doublenegative.com.au/jumpstart/

On 23/04/2008, at 6:54 AM, Christoph Jäger wrote:

> Hi,
>
> Sorry for this long post. I spent quite some time now to try to  
> figure out how to use forms to edit/update objects the "right" way  
> (you know, simple, stable, elegant, easy to read, easy add  
> things, ...). I use Tapestry 5.0.11. I am almost satisfied with what  
> I came up with now, but some improvements need to be done. Maybe  
> someone on this list can help with some ideas.
>
> As an example, lets have a PersonEdit page, with a PersonDao  
> injected. You can create new Person entries or edit existing ones.  
> To edit an existing person, there is a PageLink on a PersonSearch  
> page, which has the Person's primary id as context. To create a new  
> Person, a PageLink with 0 as context is used. To make this work, we  
> have onActivate() and onPassivate() methods in PersonEdit:
>
> void onActivate(int id)
> {
>  _id = id;
>  if (id == 0)
>  {
>    _person = new Person();
>  }
>  else
>  {
>    _person=personDao.get(id);
>  }
> }
>
> int onPassivate()
> {
>  return _id;
> }
>
> This way we can avoid using @Persist on the Person property  
> (because, for instance, we want a user to be able to open two  
> browser windows, viewing two different Person entries side by side  
> and edit and save both of them. I think this would be problematic if  
> we use @Persist, but please correct me if I am wrong).
>
> Now, editing an existing user works like this:
>
> - click the "edit user XYZ" PageLink on the PersonSearch page
> - in onActivate(), the personDao is used to query the Person from  
> the database
> - an HTML form is rendered to let the user edit the values
>
> Up to here everything looks perfect.
>
> - the user edits the Person's data and hits the "save" button
>
> - onActivate() is called, a fresh Person is loaded from the database
> - for each field in the HTML form, validation is done (if defined),  
> and a property in the fresh Person instance is set if the validation  
> was successful
> - onValidateForm() is called if existing to allow for cross-field  
> validation
> - if all validations were successful, onSuccess() is called. Here we  
> call _person=personDao.save(_person). This save method returns a new  
> Person instance, exactly as it was written to the database (primary  
> id may be generated by the database, time stamps or version numbers  
> updated, ...). We use this new Person's id : _id=_person.getId() to  
> make sure we have the correct if for the next onPassivate()
> - onPassivate() is called
> - result sent to browser, redirect
> - onActivate() loads Person again
> - new form is rendered
>
> This is good, but I think it could be improved.
>
> 1. The Person is loaded from the database twice (using  
> personDao.get()), and saved once. The save() method of personDao  
> already gives us a new, fresh instance of Person, it seems like a  
> waste to load it again after the redirect.
>
> 2. During validation, we check if there is already a Person with the  
> same userid (in onValidateForm(), or in onValidateFromUserId()) and  
> warn the user if this is the case. But what happens if a new Person  
> with this userId is added just after onValidateForm() is called, but  
> before onSuccess() is called, where we want to save? To make our  
> program solid, we have to take this into account. In case save()  
> does not work, we do not want to see some exception page, but the  
> form, as it was filled, with a hint what might have gone wrong, so  
> the user can try again. To make this possible, we have to move the  
> save() to onValidateForm(), because there we can still record form  
> errors (I think there is a JIRA for an improvement to this situation).
>
> 3. We want to give the user feedback of what happened. After  
> clicking the save button, we want to show a message like  
> "Successfully saved new person information" above the form (at the  
> same place you would see error messages like: "The first name is  
> mandatory"). The only thing that comes to my mind is to use @Persist  
> to save the message from the onSuccess() or onValidateForm() through  
> the redirect to the next page render. But now I have problems to  
> make sure this @Persist'ed message is cleared and is only presented  
> to the user directly after the save button was clicked.
>
>
> I think to find a nice solution for these issues (you know, easy  
> things should be easy to do, and a form like this does not sound  
> very complex to me, so there should be an easy solution), a  
> @Persist, which persists values just through the redirect would be  
> very helpful. The problem is, I have no idea how this could work.  
> But maybe an option like this already exists (I am sure I am not the  
> first to have problems like this), and I just didn't find it.
>
> Any ideas are welcome,
>
> Thanks,
>
> Christoph Jäger
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: T5: Forms - Best Practice

Posted by Sven Homburg <ho...@googlemail.com>.
good question, waiting for the answer too.

PS: if you attach the Autocomplete mixin  to a textfield,
the onActivate called once more, if user put some data into this field.

2008/4/22, Christoph Jäger <ch...@derwald.at>:
>
> Hi,
>
> Sorry for this long post. I spent quite some time now to try to figure out
> how to use forms to edit/update objects the "right" way (you know, simple,
> stable, elegant, easy to read, easy add things, ...). I use Tapestry
> 5.0.11. I am almost satisfied with what I came up with now, but some
> improvements need to be done. Maybe someone on this list can help with some
> ideas.
>
> As an example, lets have a PersonEdit page, with a PersonDao injected. You
> can create new Person entries or edit existing ones. To edit an existing
> person, there is a PageLink on a PersonSearch page, which has the Person's
> primary id as context. To create a new Person, a PageLink with 0 as context
> is used. To make this work, we have onActivate() and onPassivate() methods
> in PersonEdit:
>
> void onActivate(int id)
> {
>  _id = id;
>  if (id == 0)
>  {
>    _person = new Person();
>  }
>  else
>  {
>    _person=personDao.get(id);
>  }
> }
>
> int onPassivate()
> {
>  return _id;
> }
>
> This way we can avoid using @Persist on the Person property (because, for
> instance, we want a user to be able to open two browser windows, viewing two
> different Person entries side by side and edit and save both of them. I
> think this would be problematic if we use @Persist, but please correct me if
> I am wrong).
>
> Now, editing an existing user works like this:
>
> - click the "edit user XYZ" PageLink on the PersonSearch page
> - in onActivate(), the personDao is used to query the Person from the
> database
> - an HTML form is rendered to let the user edit the values
>
> Up to here everything looks perfect.
>
> - the user edits the Person's data and hits the "save" button
>
> - onActivate() is called, a fresh Person is loaded from the database
> - for each field in the HTML form, validation is done (if defined), and a
> property in the fresh Person instance is set if the validation was
> successful
> - onValidateForm() is called if existing to allow for cross-field
> validation
> - if all validations were successful, onSuccess() is called. Here we call
> _person=personDao.save(_person). This save method returns a new Person
> instance, exactly as it was written to the database (primary id may be
> generated by the database, time stamps or version numbers updated, ...). We
> use this new Person's id : _id=_person.getId() to make sure we have the
> correct if for the next onPassivate()
> - onPassivate() is called
> - result sent to browser, redirect
> - onActivate() loads Person again
> - new form is rendered
>
> This is good, but I think it could be improved.
>
> 1. The Person is loaded from the database twice (using personDao.get()),
> and saved once. The save() method of personDao already gives us a new, fresh
> instance of Person, it seems like a waste to load it again after the
> redirect.
>
> 2. During validation, we check if there is already a Person with the same
> userid (in onValidateForm(), or in onValidateFromUserId()) and warn the user
> if this is the case. But what happens if a new Person with this userId is
> added just after onValidateForm() is called, but before onSuccess() is
> called, where we want to save? To make our program solid, we have to take
> this into account. In case save() does not work, we do not want to see some
> exception page, but the form, as it was filled, with a hint what might have
> gone wrong, so the user can try again. To make this possible, we have to
> move the save() to onValidateForm(), because there we can still record form
> errors (I think there is a JIRA for an improvement to this situation).
>
> 3. We want to give the user feedback of what happened. After clicking the
> save button, we want to show a message like "Successfully saved new person
> information" above the form (at the same place you would see error messages
> like: "The first name is mandatory"). The only thing that comes to my mind
> is to use @Persist to save the message from the onSuccess() or
> onValidateForm() through the redirect to the next page render. But now I
> have problems to make sure this @Persist'ed message is cleared and is only
> presented to the user directly after the save button was clicked.
>
>
> I think to find a nice solution for these issues (you know, easy things
> should be easy to do, and a form like this does not sound very complex to
> me, so there should be an easy solution), a @Persist, which persists values
> just through the redirect would be very helpful. The problem is, I have no
> idea how this could work. But maybe an option like this already exists (I am
> sure I am not the first to have problems like this), and I just didn't find
> it.
>
> Any ideas are welcome,
>
> Thanks,
>
> Christoph Jäger
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>


-- 
with regards
Sven Homburg
http://tapestry5-components.googlecode.com