Here's Mark V of the single page edit. In previous attempts I was clinging onto client-side persistence of the entity to ensure optimistic locking, and then "the penny finally dropped" - all you need to ensure optimistic locking is to ensure the entity's version is retained - and you can do that with a hidden field. So client-side persistence really isn't necessary. Hallelujah!

        private Long _personId;

        private Person _person;

        void onActivate(Long id) throws Exception {
                _personId = id;
                _person = getPersonService().findPerson(_personId);
        }

        Long onPassivate() {
                return _personId;
        }

        void onValidateFromForm() {
                if (...a bit of validation logic detects an error...) {
                        _form.recordError(...);
                        return;
                }
                try {
// move this back to onSuccess() once TAPESTRY-1972 has been resolved.
                        getPersonService().changePerson(_person);
                }
                catch (Exception e) {
                        _form.recordError(ExceptionUtil.getRootCause(e));
                }
        }

        Object onSuccess() {
                _nextPage.onActivate(_personId);
                return _nextPage;
        }

        void cleanupRender() {
                _form.clearErrors();
        }

and in the html form, use the Hidden component that's been discussed in this mailing list:

                <t:hidden t:id="version" value="person.version"/>

or if you're using BeanEditForm:

        <t:beaneditform t:id="form" object="person" submitLabel="Save">
                <t:parameter name="version">
                        <t:hidden t:id="version" value="person.version"/>
                </t:parameter>
        </t:beaneditform>

Yes, onActivate(...) is called twice if there's an error detected server-side, but that's due to the redirect-after-post paradigm, so it's a cost with a pretty big benefit.

One last note, the cleanupRender() method can be removed if T5 moves to "flash" persistence on the form's ValidationTracker.

Anything I've missed?

Cheers,

Geoff

On 12/12/2007, at 7:39 AM, Geoff Callender wrote:

I'm planning on doing one, but not today. If you know your way around T4 then the web flow example in JumpStart Max 2.0.0 may help you. It's at http://files.doublenegative.com.au/jumpstart/ .

Here's Mk IV of the single page edit. It seems that recording errors in onSuccess() can cause fields to revert when you redisplay with error, so I've moved some of it to onValidate(). The notes from before are still relevant.

        private Long _personId;
        
        // "client" is used so that  even if you use the Back button
        // to get to me, I will have the right Person object.
        @Persist("client")
        private Person _person;
        
        void onActivate(Long id) throws Exception { _personId = id; }

        Long onPassivate() { return _person.getId(); }

        void setupRender() throws Exception {
                if (!_form.getHasErrors()) {
                        _person = getPersonService().findPerson(_personId);
                }
        }

        void onValidate() throws Exception {
                if (...a bit of validation logic detects an error...) {
                        _form.recordError(...);
                        return;
                }
                try {
                        getPersonService().changePerson(_person);
                }
                catch (Exception e) {
                        _form.recordError(ExceptionUtil.getRootCause(e));
                        return;
                }
        }
        
        Object onSuccess() {
                _nextPage.onActivate(_personId);
                return _nextPage;
        }

        void cleanupRender() {
                   _form.clearErrors();
        }

Cheers,

Geoff

On 12/12/2007, at 4:53 AM, jeffrey ai wrote:


Geoff, I think your code is great for **ONE** edit-page.
Our project is looking for a web flow from T5, like the Spring one.
Do you have any idea about it?
Howard mentioned he may add this feature in the next release.
Might be a little late to us, but I am expecting to see it.

Cheers,
Jeffrey Ai


Geoff Callender-2 wrote:

Hi,

In search of best practice for an "edit" page, here's my 3rd attempt.
It's looking pretty clean-cut to me, but I'm looking for suggestions
on how to improve it further.

        private Long _personId;
        
        @Persist("client")
        // Persistence is needed here because this is a detached Entity
Bean.  When we call the service to
// accept our changes, it will need its id and version fields intact
to be able to do optimistic
        // locking check and a successful merge. "client" is used so that
even if you use the Back button
        // to get to this page, we will have the right Person object.
        private Person _person;
        
        void onActivate(Long id) throws Exception { _personId = id; }

        Long onPassivate() { return _person.getId(); }

        void setupRender() throws Exception {
                if (!_form.getHasErrors()) {
                        _person = getPersonService().findPerson(_personId);
                }
        }

        void onValidate() throws Exception {
                if (...a bit of validation logic detects an error...) {
                        _form.recordError(...);
                }
        }
        
        Object onSuccess() {
                try {
                        getPersonService().changePerson(_person);
                        _nextPage.onActivate(_personId);
                        return _nextPage;
                }
                catch (Exception e) {
                        _form.recordError(ExceptionUtil.getRootCause(e));
                        return null;
                }
        }

        void cleanupRender() {
                   _form.clearErrors();
        }
        
Some notes:

1. Detached object - Person is a detached entity.  I am deliberately
avoiding refreshing it every time in setupRender() because a) it would
overwrite the user's changes, and b) it would defeat optimistic
locking: if someone else has changed the object then I DO want
getPersonService().changePerson(_person) to reject the transaction
when Save is pressed.

2. Thread-safety - I'm using "client" persistence to avoid the whole
thread-safety issue caused by the user opening a new window or tabs in
same browser (T5 can't tell them apart so they share the same
HttpSession).

3. onPrepareFromForm() - I'm avoiding it because it gets called too
often (something to do with "rewind"?).  setupRender() seems better
for the job.  Any downside t this?

4. cleanupRender() - if/when 5.0.7 uses flash persistence on the
form's ValidationTracker then I'll ditch this method.

Suggestions please!

Thanks,

Geoff




--
View this message in context: 
http://www.nabble.com/T5%3A-Edit-page-best-practice---Mk-III-tp14249141p14279495.html
Sent from the Tapestry - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



Reply via email to