GWT 2.1 is advertized as providing "a framework for business apps", and one of the included features is the Editor framework, which provides data binding between bean-like objects and UI fields.
On one hand you have an object graph that you want to display or edit, and on the other hand a set of widgets (let's call them editors, we'll see the why later). To use them together so that your objects are displayed in the editors, you'll use an editor driver.
GWT 2.1 comes with two editor drivers: the
SimpleBeanEditorDriver and the
RequestFactoryEditorDriver. Similarly to
UiBinder, you'll declare an interface extending one of these with generic parameters.
The kind of objects that you can edit depends on the editor driver you use, but in any case they'll be bean-like objects.
SimpleBeanEditorDriver edits any simple Java bean while
RequestFactoryEditorDriver expects only RequestFactory proxies; and in case that's not enough (really?), it's technically possible to create your own editor driver flavor (and it's not that much work, though you have to be familiar with the editor framework internals).
Finally, editors are any class that implement the
Editor marker-interface or the
IsEditor interface. There are further constraints and features, and that's the whole point of this article, as we'll see below.
Both editor driver interfaces have two generic parameters: the type of the object being displayed or edited (I'll always use edited from now on, but editors can be read-only, and thus only display data), and the type of the root editor.
The GWT Editor framework makes extensive use of code generation. Because everything is done at compile-time by inspecting classes, it means everything is based on the types you use in your interface declaration (and for sub-editors, see below, the field and method declarations). If you then use the generated code on a subclass of the editor for instance, only the bits inherited (and possibly overridden) from the declared editor type.
The Editor framework uses a Flow Synchronization pattern: you edit an object for the editor driver to populate editors from the object's values, and you later flush the editor driver to copy editors' values back into the edited object. During a flush, editors have an opportunity to report errors that will be available as
SimpleBeanEditorDriver provides just that: an
edit() method, a
flush() method, and a pair of
getErrors() accessors. But before you
edit() an object, you'll first have to
initialize() the editor driver with the actual editor instance to be used.
We'll look at the
Editors are a graph of objects used to edit objects. Each editor is bound to a type of object that it can edit. Starting from a root editor type, the editor driver generator will build a representation of the editors tree using reflection: any non-private field or non-private no-args method whose (return) type is an editor will be seen as a sub-editor, unless it is annotated with
@Ignore. The name of the edited property will be infered from the field or method name (it will be field/method name after an optional
Editor suffix has been removed), unless it is annotated with
@Path (which can use the dotted notation to reference a sub-property; in GWT 2.2 you'll also be able to use the empty string to reference the current object). At runtime, if any of these sub-editors is null, it will simply be ignored. This is known as the Editor contract.
A field or method will be treated as an editor if its type implements the
Editor marker interface (or one of its subinterfaces, see below) or the
IsEditor interface. There are several kinds of editors:
IsEditorreturns an Editor from its asEditor() method. When building its editors tree, the editor driver generator will descend into the
LeafValueEditorrepresents a leaf in the editors tree. The editor driver generator won't try to find sub-editors.
CompositeEditorcontains an unknown, dynamic number of sub-editors of the same type. There are several built-in
CompositeEditorto edit an optional value or conveniently adapt a
Listof objects: the
ListEditorwith which you manage the sub-editors display, and the
HasDataEditorwhich uses a Data Presentation widget (such as a
CellTable) to edit the objects in the list.
Editors can also implement a couple of other interfaces to extend their behavior:
HasEditorDelegatewill be passed an
EditorDelegateon which it can report errors and subscribe for changes to the edited object (only supported by the
HasEditorErrorswill be notified of errors to display them to the user
ValueAwareEditorwill be notified of the value being edited (otherwise, the Editor framework simply descends into sub-editors down to
LeafValueEditors) and changes to its properties (currently unimplemented AFAICT), and be given the opportunity to do some additional work during the
flush()of the editors back to the edited objects.
Most widgets displaying or editing values are already editors (most are actually
IsEditors of editor adapters): TextBox, Label, DateBox, CheckBox, etc. and a few widgets have been added to add type specialization: IntegerBox, DoubleBox, NumberLabel, DateLabel, etc. This makes the Editor framework a very good friend of UiBinder!
Integration with RequestFactory
RequestFactoryEditorDriver is similar to the
SimpleBeanEditorDriver but with a few key differences:
- First, the
initialize()method can also take a
EventBusto listen to
EntityProxyChangeevents; it will then
find()the object back from the server and refresh the editors.
edit()method expects both the object to be editor and a
RequestContext(so the driver can ensure all objects in the graph are mutable, by calling the
- If you just want to display a proxy, you can use the
display()method, which works seamlessly with immutable objects.
flush()method doesn't return the edited object, but the
RequestContextpassed to the
getPaths()method returns the list of all the properties being edited, and can be passed to a
with()to only ask the server for those properties.
- Finally, you can display errors reported out-of-band (e.g. by the server) with the
setViolations()method. Note that in GWT 2.2 this method is expected to be deprecated and replaced with a
setConstaintViolationsthat will also be available on the
The GWT Editor framework is a real time-saver, because you no longer have to maintain the code to push your objects' properties into widgets and back to the objects, and you therefore no longer have to test that code (because as soon as you add a field, you'll probably copy/paste lines and can miss a change, injecting a property into the wrong field, or a field's value into the wrong property). As a proof of that, I've quickly drafted a before/after sample (only files that are necessary to understand the code are included, and I know there are typos and missing imports, but using this user script or Chrome extension you can then easily see a diff between the 2 versions)