On Thursday 06 March 2008 18:59:59 graemer wrote:
So as some of you may know I've been updating the Grails Wicket plug-in.
I didn't know but I tried the plug-in a couple of weeks ago (with help of your
blog) and it worked fine expect that I couldn't make wicket classes reload. I
should look at the updated version ;)
There is quite a few users on the Grails list who have shown interest in it
and the ability to use a component framework backed onto the rest of the
Grails stack (GORM, transactional services, plugin system etc.)
However, the plugin provides a very basic level of integration and I was
thinking it would be nice to create a Groovy DSL for Wicket. I looked into
the WicketBuilder, but really I think we can take this further. So based on
the example here:
http://www.theserverside.com/tt/articles/article.tss?l=IntroducingApacheWic
ket
I came up with the below syntax to represent this application (note i
havent actually implemented anything yet this is just a syntax proposal).
Thoughts/feedback/suggestions welcome:
I also looked a while ago at the WicketBuilder, Kevin Galligan blog and
discussion at wicket and groovy mailing lists.
As I understand there are two major problems with using groovy and wicket:
- groovy doesn't have anonymous classes but they are used a lot in wicket;
- if you somehow substitute anonymous classes with closures (what seems to be
the most sane way), groovy's closures are not serializable but components in
wicket must be serializable.
So IMHO the main purpose of creating a builder (actually it doesn't have to be
a builder) would be in the first place to solve the above problems.
From what I remember from the WicketBuilder sources to solve the problems
WicketBuilder:
- generates groovy class (as text) which implements/overrides methods, then
builder add closures calls to this methods.
- stores the class of the closure (class is serializable :)) and when the
closure needs to be called, builder creates new instance of the class and
execute it. The consequence is that closures cannot reference local
variables.
Some time ago I myself created a small builder to try groovy out and see how
useful nice syntax can be. To solve the above problems I:
- manually created subclasses for some basic wicket classes, added closures
calls in implemented methods and added setters for these closures. (I know
manual subclassing is lame, but it was the simplest thing I could
do.)
- created closure wrapper which is serializable and can serialize closure
casting all its properties to Serializable. This way closures can reference
page's local variables. It works fine for my simple use cases, though it
might be not a very good solution.
[skipped]
Contact.findByNameLike(%${params.searchString}%) } as
LoadableDetachableModel
[skipped]
view label(lastName, new PropertyModel(item.model,
lastName))
item view
item link(edit, item.model) {
onClick {
redirect page:new EditContact(model.id)
}
}
[skipped]
If I got it right, the main difference compared to WicketBuilder is that you
put event handlers like onClick in a closure where child components are
added. I like the idea, it seems to be more readable than adding closures as
attributes to the builder method. I also like the idea of using , IMHO
it's a bit easier to notice in the code than add.
What I did in my humble builder is more weird, because I added component id to
the builder methods names. It looks like this (it's working code from
test):
builder.build {
myLabel()
myLink(onClick: this.callback) {
my2Label()
}
myForm(onSubmit: this.callback) {
anotherLabel()
myButton(onSubmit: this.callback)
myList([1,2,3], onItem: {})
}
}
I used here link to instance method (this.callback) because I didn't like
putting callback code in the builder hierarchy.
The next idea was that it would be ugly to put all the configuration into
builder, so after calling the builder it's possible to access components
without declaring local variables (it only works on a groovy specific page):
builder.build {...}
myLabel.visible = false
myLabel.markupId = myLabelMarkupId
Another idea is that there should _not_ be any builders since components
hierarchy is already declared in markup files. In this case components could
be created like that (it's also working code from test):
public PageWithImplicitLabel() {
// creating label by calling non-existent method
myLabel(label text)
// accessing label by name
myLabel.markupId = myLabelMarkupId
}
Component hierarchy could be built at the end of the constructor with static
call like that:
public PageWithImplicitLabel() {
...
HierarchyBuilder.build(this); // reads markup, rearranges components
}
I've also created very basic HierarchyBuilder. I wonder if someone really use
something