Using equanda, it is possible to easily generate very complex forms from your domain model.
Such form can have inline display of referenced records, can have client-side switching between display and edit mode, full support for linked records etc.
Unfortunately, this is the theory. It seems that in the last few weeks, some things had gone wrong and certain features stopped working. I have spent the last couple of days trying to track these problems down.
Part of the problem being that it was not known when exactly certain forms stopped working. It had happened in the last month or so, but a lot has changed in this period. Including an upgrade to the GA version of tapestry 5, various enhancements in the forms, including the display/view switching, improvements in some components to make them better and/or faster etc.
Happy hunting.
I was facing two problems.
- In inline components, the data entered by the user was not persisted
- An exception occured in some cases when adding items in a (multiple) link field
The second problems seemed easy enough to solve. The loops in the form were marked as volatile. The exception clearly had something to do with that, so changing the loops tot non-volatile seemed to fix that.
For the first problem I wrote a small test application to verify that saving values works when the values to save are stored in container objects. This proved to work just fine, but only when volatile is true.
Documentation says (loop) :
If volatile is true and the Loop is enclosed by a Form, then the normal state saving logic is turned off. Defaults to false, enabling state saving logic within Forms.
or (grid)
If true and the Grid is enclosed by a Form, then the normal state persisting logic is turned off. Defaults to false, enabling state persisting within Forms. If a Grid is present for some reason within a Form, but does not contain any form control components (such as org.apache.tapestry5.corelib.components.TextField), then binding volatile to false will reduce the amount of client-side state that must be persisted.
I think the last part is wrong and should be “binding volatile to true reduces the amount of client-side state”.
Apart from that, it only hints at the problem. When volatile is false, the components seem to be individually serialized to the client. The end result is that the data is stored in deserialized objects which are no longer combined in a list (which is looped). When volatile is true, the objects are not serialized (thus less client-side state), but the form remembers the number of items in the loop. If this changes server side, then uninformative errors occur. So when using objects in a collection which are looped and need updating, volatile has to be set to true.
The caveat of course is that the collection needs to be either persisted or rebuilt.
So, volatile needs to be be true according to the test application. Problem is that it doesn’t make a difference in the full application and causes the second problem I needed to fix. Hmmm.
Tests indicated that the list was iterated and that all the updates were done at the end, all in the same object, overwriting previously stored values. This contrary to the test application.
More tests showed this to be the case for the fields in the embedded component, but not for separate TextField components in the loop.
Then the penny dropped, the component uses FormFragments. These dynamically include parts of a form on the client side, when visualized. Apparently this does not consider the nesting in the form. When changing the component to not use the FormFragment component, the first problem was solved.
Unfortunately, as all loops were volatile again, the second problem has resurfaced and it seemed like I had to choose which bug to solve and which to keep.
Back to square one. I had to figure out which component on the page caused the problem (which “loop”). By accident, I changed the order in which I do my tests, and this helped to pinpoint the offending loop. This was a PagedLoop which seems to have some changing state between requests. Replacing this with a plain Loop component solved (though it changes the display).