Update 7/8/2010 - MyEclipse for Spring 8.6 now generates full ready-to-run GWT applications based on MVP and UI Binder in minutes.  Just point the scaffolding wizard at your database tables, Java beans, or JPA Entities.  You can learn more about it from the Generating Enterprise Class GWT applications for Spring post that I wrote on Genuitec blog.


About a week ago I posted the following question on GWT forums.

"Does anyone have an MVP sample project (Contact) that is implemented using UIBinder?  I'm starting to get my head around MVP and UIBinder, but I'm struggling with the best way for using both.  A sample project would be really helpful."

[SOURCE INCLUDED]

 


 

As I mentioned in my Skyway Builder extension for GWT – spring4gwt and GWT event buses blog post at the Skyway Builder community site, I've really been interested in understanding the best practices of GWT application development.  The following Google IO presentation really has some excellent information, and Ray Ryans introduced the Model-View-Presenter (MVP) architecture pattern.

Following the Google IO presentation there was a lot discussion of the MVP pattern, however the discussions were mostly centered on developer's interpretations of the pattern.  I couldn't find a concrete implementation, and there certainly wasn't one provided by Google.

When GWT 2.0 came out, I discovered a thorough presentation of the MVP pattern and a sample project (Contacts) at the GWT website.  I studied it, and the pattern started to make sense to me.  However GWT 2.0 also included UIBinder, a new feature for defining user interfaces declaratively.  What I really wanted was an implementation of the sample project with the UI implemented using UIBinder.

So I waited a few days for a response to forum post, but I got no response.  So I needed to roll-up my sleeves and figure it out myself.

The following four files can be copied directly into the sample project.  The java classes will replace the existing java classes, and the xml files should be located in the same folder.  I didn't put any emphasis on the CSS.  While the views are structurally the identical, they don't look exactly like the original.

ContactsView.java

package com.google.gwt.sample.contacts.client.view;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.sample.contacts.client.presenter.ContactsPresenter;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.Widget;

public class ContactsView extends Composite implements ContactsPresenter.Display  {

	private static ContactsViewUiBinder uiBinder = GWT
			.create(ContactsViewUiBinder.class);

	interface ContactsViewUiBinder extends UiBinder {
	}

	@UiField
	Button addButton;

	@UiField
	Button deleteButton;

	@UiField
	FlexTable contactsTable;
	
	public ContactsView() {
		initWidget(uiBinder.createAndBindUi(this));

	}

	@UiHandler("contactsTable")
	void onContactsTableClick(ClickEvent e) {
		//Window.alert("Hello CLICK TABLE!");
	}
	
	@UiHandler("addButton")
	void onAddButtonClick(ClickEvent e) {
		//Window.alert("Hello ADD!");
	}

	@UiHandler("deleteButton")
	void onDeleteButtonClick(ClickEvent e) {
		//Window.alert("Hello DELETE!");
	}

	@Override
	public Widget asWidget() {

		return this;
	}

	@Override
	public HasClickHandlers getAddButton() {

		return addButton;
	}

	@Override
	public HasClickHandlers getDeleteButton() {
		return deleteButton;
	}

	@Override
	public HasClickHandlers getList() {
		return contactsTable;
	}

	@Override
	public void setData(List data) {
	    contactsTable.removeAllRows();
	    
	    for (int i = 0; i < data.size(); ++i) {
	      contactsTable.setWidget(i, 0, new CheckBox());
	      contactsTable.setText(i, 1, data.get(i));
	    }
		
	}

	@Override
	  public int getClickedRow(ClickEvent event) {
		    int selectedRow = -1;
		    HTMLTable.Cell cell = contactsTable.getCellForEvent(event);
		    
		    if (cell != null) {
		      // Suppress clicks if the user is actually selecting the 
		      //  check box
		      //
		      if (cell.getCellIndex() > 0) {
		        selectedRow = cell.getRowIndex();
		      }
		    }
		    
		    return selectedRow;
		  }

	@Override
		  public List getSelectedRows() {
		    List selectedRows = new ArrayList();
		    
		    for (int i = 0; i < contactsTable.getRowCount(); ++i) {
		      CheckBox checkBox = (CheckBox)contactsTable.getWidget(i, 0);
		      if (checkBox.getValue()) {
		        selectedRows.add(i);
		      }
		    }
		    
		    return selectedRows;
		  }
	
}

EditContactView.java

package com.google.gwt.sample.contacts.client.view;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.sample.contacts.client.presenter.EditContactPresenter;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.TextBox;

public class EditContactView extends Composite implements EditContactPresenter.Display  {

	private static EditContactViewUiBinder uiBinder = GWT
			.create(EditContactViewUiBinder.class);

	interface EditContactViewUiBinder extends
			UiBinder {
	}

	@UiField
	TextBox emailAddress;

	@UiField
	TextBox firstName;

	@UiField
	TextBox lastName;
	
	@UiField
	Button saveButton;

	@UiField
	Button cancelButton;
	
	public EditContactView() {
		initWidget(uiBinder.createAndBindUi(this));
	}

	@UiHandler("cancelButton")
	void onCancelButtonClick(ClickEvent e) {
		//Window.alert("Hello!");
	}
	@UiHandler("saveButton")
	void onSaveButtonClick(ClickEvent e) {
		//Window.alert("Hello!");
	}

	@Override
	public Widget asWidget() {
		return this;
	}

	@Override
	public HasClickHandlers getCancelButton() {
		return cancelButton;
	}

	@Override
	public HasValue getEmailAddress() {
		return emailAddress;
	}

	@Override
	public HasValue getFirstName() {
		return firstName;
	}

	@Override
	public HasValue getLastName() {
		return lastName;
	}

	@Override
	public HasClickHandlers getSaveButton() {
		return saveButton;
	}

}

ContactsView.ui.xml

EditContactView.ui.xml