Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Thursday, February 15, 2007

T5 Screencast #4: BeanEditForm

Just finished a new screencast on BeanEditForm: Tapestry 5 Screencast #4.

In twelve minutes (including some lovely fumbling around the 8 minute mark), I only scratch the surface of what BeanEditForm can do, and it's still not quite as powerful as Daniel Gredler's BeanForm component for Tapestry 4 ... but it is easy and fast and full of fun tricks.

Introducing this component first, and only incidentally talking about Label and TextField, is according to plan. I want people to get hooked on Tapestry 5's power and ease of use immediately.

Next up: the Grid component.


Jacob Hookom said...

awesome awesome awesome-- i would love to see this in JSF 2.0, but with JSR 299 and 303 backing some of the functionality you've presented. Also t:facet would be much smaller than t:parameter (don't ask me where facet comes from) ;-)

Craig said...

Been watching the blog since the first screen cast and every post makes me more and more excited about T5 - can't wait and keep it up!

Howard said...

JSF facets are not quite the same as Tapestry parameters; facets are more strongly typed sub-objects (as I understand it), whereas parameters turn into an opaque type, Block. Block has no methods but can be returned from a render phase method to force the Block to render at a particular point in the render.

In some ways, its about the whole being more than the sum of the parts. Layering the BeanModel with Tapestry Blocks (via the parameters) and with the localization support, and mixing in the case insensitivity so thatyou don't have to second guess the case of things ("userId" or "userid"?) means that everything just fits together.

I am a little annoyed by the fact that your have to restart for non-component class changes. That part of the design is constrained by the way ClassLoaders work (it's a very leaky abstraction). I could load from the data package using the same class loader as is used for pages and components, but then the objects would not be assignment compatible with the services layer (meaning you would get ClassCastExceptions, as you sometimes get with JSPs). So, in the meantime, you have to restart more than I'd like (at least it is fast!).

Thanks for the compliments!

Jacob Hookom said...

JSF facets can be strongly typed, but aren't by API Map<String,UIComponent>. Strongly typed components usually come into play from the parent/children relationship such as a HtmlTable to UIColumn children, the UIComlumn can have anything for its 'header' and 'footer' facet. I think ADF is the only library I know of where it asserts facets of a specific type, but then again, you are free to do whatever you want with a clean API ;-)

Anonymous said...

It would also be nice to optionally specify which fields need to be edited and the order.
Relying on the getter methods order would most likely cause some headache in the long run. One has to remember that the order might impact the form.

Other suggestion about the parameter: How about t:param?

my $0.02


Chuck said...

Looks very nice so far. Will there be some way of adding other scopes to @ApplicationState other than the HTTP session? I'm thinking specifically of Seam's conversation scope, which I'm getting quite a bit of mileage out of in my current app.

Bill Holloway said...

Yah, awesome component, but I do echo the concern about using ordering of the getter methods to specify the ordering of the fields in the component.

How about letting the getter ordering be the default but also having an optional parameter to beanform be a list of Strings of bean object property names to specify ordering?

Howard said...

BeanModel is a collection of PropertyModel. PropertyModel includes an order property, the presentation order of the property within the bean. The default for order comes from getter position, unless overriden by the @Order annotation. Further, if you explicitly create the BeanModel, you can adjust the properties as you see fit before passing it to the BeanEditForm.

But, yes, a simple parameter to for setting the order explicitly would be nice as well. Low priority at this point.

christian said...

Fine sc.

What caught my eye is the fact, that Tapestry seems to move away from the idea of providing a HTML which can be used as static prototype. Is my asumption right, or was it only in this screencast?

Another side question: Are you still using Jetty 5 with the respective JettyRunner plugin? Me for myself was not able to find something working with j6/eclipse 3.2...

Howard said...

You can still toss into the template excess HTML for preview purposes (inside a t:block element to keep it out of the way). I'll probably be adding something like T4's $content$ and $remove$ in the future.

But choosing between iffy previewability and raw bang-for-your-buck, you can guess which side I'll choose.

I've said it before: we need to "hook" users within the first few minutes. I won't do anything flashy for the sake of being flashy, but I will bring in lots of ease-of-use and show that first, before the nuts-and-bolts details.

shaolang said...

While this screencast is awesome, but I find distaste in using the .properties file to change the texfield labels. Is there any reason for not using annotations?

Howard said...

Shaolang -- is that a joke?

I think, and most would agree, changing the labels using the message catalog is the best approach, especially in light of eventually localizing the application (providing translations for different locales).

You still have the option of overriding the default behavior (in this case, the default labels obtained from the message catalog) by directly editting the BeanModel. I.e.:

model.get("userId").label("Account Id");

... but I'd much rather put:

userId-label=Account Id

in a .properties file and not have to override & second guess the BeanModelSource service (which generates the BeanModel in the first place).

Olivier said...

Very nice! Started with the tutorial on apache and now I'm here.

I've tried the example in your screencast but I get an exception. I'm guessing that this feature isn't available yet.

Then completely off topic. If it would become available how do I update my libs with maven? I am quite new to this.

shaolang said...

Actually, I woke up realizing my question was really stupid... :p

T5 still looks awesome though... :)

cmittendorf said...

very nice, especially the validation stuff. what I would like to see is some framework support for layered validation like annotating properties at the lowest level as seen in this screencast. some mechanism for validating the whole bean as the next layer and some kind of methods for validating all form beans at the highest level. though it might get kind of tricky if you have some dependencies between some of the beans. however, i want more screencasts ;-) keep coding!

Howard said...

I think there are limits to how much of the validation, especially cross-form validation, can be accomplished declaratively, especially once you start introducing JavaScript on the client side (especialy Ajax). My goal is to bring the declarative to where its perfectly good for most cases, and then make it easy to do the rest in clean, simple code (via event handler methods).

Olivier said...

How does one actually use the submitted values? I was thinking that it might be like an ActionLink, just add an id and you can catch the form with @OnEvent but that's not it.

I really would appreciate an answer to that or a hint so that I can figure it out for myself.

Olivier said...

@OnEvent("submit") is the answer. There is an example here: http://tapestry.apache.org/tapestry5/tapestry-core/

Howard said...

Yep, I'm lagging on the documentation . Often the test suite is a good way to see proper examples.

In this case, the Form component embedded inside the BeanEditForm will trigger a "success" event, which will propogate up to your page (or whatever the container of the BeanEditForm is).

Which raises an interesting question: when an event "bubbles up", what should be considered the originator of the event? As I'm writing this, I'm thinking that a bubbled event should appear to have originated BeanEditForm, not the (hidden) Form within. Otherwise it's very hard for the container that recieves the bubbled up event to know from where it orignates (this could be a problem if you mix a BeanEditForm with an ordinary Form within the same container).

Olivier said...

The submit event is also triggered when the form does not validate. How can one verify if it passed the validation by annotation?

And before I start posting every single question here is there a forum where such questions can be asked? Or are there only the mailing lists? Maybe I'll just have to wait that documentation matures somewhat...

Howard said...

Please check the Form documentation for the events it emits. Also, it's much easier for me to answer questions on the mailing list; you'll find quite a few T5 questions and anwers there.

Olivier said...

I just found my own answer again. There are validation, success/failure and submit events. I really should try harder before coming here.

I'll subscribe to the mailing list and check out the archives first. Thank you for your effort!

Steve said...

Off topic question. :-) I was wondering how you get Eclipse to generate getters and setters properly when you use the underscore prefix naming convention? I use the same convention but I could never figure out how to get it to generate them correctly.


Howard said...

On the Java / Code Style preferences pane, you can list prefixes and/or suffixes for different types of fields. I list "_" as a prefix on ordinary (instance) Fields.

Richard said...

Just a big thank you for all your work on BeanEditForm.

I have stolen (with due credit, of course :) your great idea of 'using Javassist to read debug line information and order the UI fields according to source code order' for the latest release of Metawidget.

Thanks again and keep up the good work!

Toby said...

It is a nice solution for creating forms for existing classes/beans. I only wish there was an easy way to generate forms dynamically if the configuration is stored in a database (e.g. website builder applications with a generic data model), e.g: http://wiki.apache.org/tapestry/WishList. If there should be a way to do this though, could you maybe cover this in the next screencast?

Howard said...

Dynamic data web sites are quite possible; I'd say even easy, once you get it set up. However, the details tend to be very application specific.

Perhaps at some point I could create a library that can be used to generate these dynamic forms, and you would be responsible for translating your internal representation into Tapestry's representation.

Toby said...

From a user perspective, something like this would be good:

source="itemsListFromDB" result="submittedItemsList">

A "FormItem" could look like that:

public FormItemType getType();
public Value getValue();
public List(Value) getValues();
public boolean isArray();
public Validator getValidator();
public ItemDisplayOption getDisplayOption();

// DropDown | Textinput | Checkbox | RadioButton |

So you would just read your configuration from the database and the you could create a list of form items:

FormItem item = new FormItem();

FormItem item = new FormItem();