Exploring data binding with Gwittir
January 30th, 2008 - Written by in Using GWT
is a library for GWT that provides a lot of interesting features including data binding, animation, reflection, and more. To help me learn more about this library, I decided to take it a spin and create a small GWT application to experiment with Gwittir’s data binding capabilities. So what does data binding give you? It allows you to glue together your user-interface widgets with the properties on your domain model. Whenever a user does something to a bound widget, its associated property in the model will be updated automatically. The binding is bi-directional as well, so if you make a change to the model, the widget will also update itself to reflect the change. Neat! So some of the benefits of data binding are that it promotes good MVC design, and it saves you from writing all those event listeners to copy data back and forth between UI and model.
The sample application I created is this simple data entry form, which allows the adding of books to a table.
I first created a Book object to be my model. It is a simple JavaBean with three fields, title
, year
, and inStock
and extends the AbstractModelBean
class. AbstractModelBean implements the Bindable
interface which allows an object to be bound by the Gwittir framework and gives us the necessary PropertyChangeSupport.
public class Book extends AbstractModelBean { private String title; private int year; private boolean inStock; public Book() {} public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } // ... other getter and setters omitted
For the user interface, I created a composite BookForm widget to hold the input fields and a table which displays the added books. The Gwittir distribution includes several BoundWidgets (Button, TextBox, etc), which are just standard GWT widgets with some extra methods to make binding easier. To get this behavior in my BookForm widget, I extended AbstractBoundWidget
. By doing this, I had to implement the getValue()
and setValue()
methods. For a composite widget like I’ve created, the value
property is simply my Book model.
(com.gwtsite.gwittir.BookForm)
public BookForm() { this.setValue(new Book()); // ... } public Object getValue() { return this.getModel(); } public void setValue(Object value) { this.setModel(value); }
Now that I have created my model and view, I need a controller to hook everything together. In Gwittir, this is called an action
. For this, I created an AddBookAction class which implements the BindingAction
interface. This interface has four methods, bind()
, unbind()
, set()
and execute()
. Lets look at the set()
method first. The set()
method gets called when our widget is initially attached to the browser window. It is responsible for doing all the dirty work and wiring up the Book properties to the widgets in our form.
The Gwittir Binding
object represents the data binding between two bindable objects. It can also hold a list of child bindings which allow us to manage multiple bindings with one instance.
(com.gwtsite.gwittir.AddBookAction)
private Binding binding = new Binding(); public void set(BoundWidget widget) { BookForm bookForm = (BookForm)widget; Book book = (Book) bookForm.getModel(); ... binding.getChildren().add(new Binding(book, "title", bookForm.titleText, "value"));
The above code demonstrates how we bind the title property of the Book object with the value property of the title TextBox of our BookForm UI widget.
Gwittir also provides support for adding validation to our bindings. I’ll demonstrate this by doing some validation on the year
property. Lets say that we only allow books published after 1950 to be added to our list. To accomplish this, I created a custom Validator by implementing the Validator interface.
(com.gwtsite.gwittir.AddBookAction)
private class NewBooksOnlyValidator implements Validator { public Object validate(Object value) throws ValidationException { String year = value.toString(); if (new Integer(year).intValue() < 1950) throw new ValidationException("Must be after 1950."); return new Integer(year); } }
Ok, so this is not truly correct code, but it will work for demonstration purposes. Now we need a way to show any validation errors. Gwittir provides a ValidationFeedback
interface along with several implementations. I decided to use two implementations – the PopupValidationFeedback
class which will display an error message next to the TextBox if the user enters an invalid year, and the StyleValidationFeedback
class which will add a css class called validation-error
which we can use to highlight any errors in red.
I can add these two implementations into a CompositeValidationFeedback
object and pass it to the binding like this:
(com.gwtsite.gwittir.AddBookAction)
ValidationFeedback feedback = new PopupValidationFeedback(PopupValidationFeedback.RIGHT) .addMessage(NewBooksOnlyValidator.class, "We only accept books published after the year 2000."); CompositeValidationFeedback cvf = new CompositeValidationFeedback() .add(feedback) .add(new StyleValidationFeedback("validation-error")); binding.getChildren().add(new Binding(book, "year", null, null, bookForm.publishedText, "value", new NewBooksOnlyValidator(), cvf));
The second method of our Action class is the bind()
method. This gets called automatically after our BookForm is attached to the browser window. All it does is call bind()
on our binding object and establishes the binding.
The last step is to call our Action’s execute()
method. I call this from BookForm’s ‘Add book’ button.
(com.gwtsite.gwittir.BookForm)
addBookBtn.addClickListener(new ClickListener() { public void onClick(Widget sender) { if (getAction() != null) { getAction().execute(BookForm.this); } } });
The execute implementation itself is actually quite simple. First it checks if the binding is valid. This will not be the case for example, if the user enters an invalid year. If the binding is valid, then we get the fully populated Book object from the form, and add it to our Table. Easy!
(com.gwtsite.gwittir.AddBookAction)
public void execute(BoundWidget widget) { BookForm bookForm = (BookForm)widget; Book book = (Book) bookForm.getModel(); if (binding.isValid()) { bookForm.booksTable.add(book); } }
Hopefully this small example will peak your interest in taking a closer look at what the Gwittir framework has to offer. It was created by Robert Cooper and Charlie Collins who have a new book coming out, called . So expect some cool stuff from this framework in the future.
References