My favorites | Sign in
Project Home Downloads Wiki Issues Source
READ-ONLY: This project has been archived. For more information see this post.
Search
for
ViewField  
Overview of the new @ViewField annotation
Updated Dec 30, 2010 by gal.dol...@gmail.com

Introduction

ViewField is the UiField equivalent to UiBinder to access your view fields.

This new concept make Guit's mvp looks more like a UiBinder with steroids.

Problem with the current approach

  • It is very easy to move a lot of logic to your views, loosing testability.
  • Hard access to the view: declare a method, implement it, maintain it.
  • It don't encourage building reusable widgets. Instead you generally end up making big and complex one-time-use Views.
  • Its very easy to write layouts in java instead of using uiBinder

New approach

  • Access your widget throw an interface
  • The view implementation disapear and get generated a uiBinder (just the ui.xml)
  • It depends on uiBinder, so you are encourage to keep your layouts in ui.xml.
  • Force to write reusable widgets when you really need them because the presenter can't instantiate widgets.
  • Possibility to decide if your uiBinder implementation will be a Composite, a Widget or none. By default it will be a composite, you can annotate the ViewInterface with @WidgetView(DivElement.class) to make it a Widget specifying the root element class as parameter. Or you can also annotate it with @StandaloneView so it won't be a Composite or either a Widget, it will just bind the ui.xml.
  • @ImplicitCast(Class.class) in a parameter of an interface will cause an implicit cast.
  • Interface emulation (details below).

The new approach force you to write better code

When you needed to show an alert with the old approach you will do it like this:

View interface:

void alert(String message);

View implementation:

public void alert(String message) {
    Window.alert(message);
}

With the new approach you cannot do that anymore. And the presenter cannot make the call Window.alert(), so you get forced to do something like this:

Interface:

public interface Alerter {
    
    void alert(String message);
}

Implementation:

public class AlerterImpl implements Alerter {

    @Override
    public void alert(String message) {
        Window.alert(message);
    }
}

Gin module:

public class AlerterModule extends AbstractGinModule {

    @Override
    protected void configure() {
        bind(Alerter.class).to(AlerterImpl.class).in(Singleton.class);
    }
}

And you can inject Alerter in any presenter and use it.

As the interface is blind you can also mock it easily in any test.

¿Why is this better?

Ok, you wrote much more code than before, but now you have a reusable "Alert" component that you can include in all your projects.

I think thats a big win!

New mvp diagram

Accessing ui: fields on your presenter

Now you can annotate a field in your presenter with @ViewField. This annotation will simply bind that field from the view to the presenter.

Example:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:d="urn:import:com.google.gwt.user.datepicker.client">

<ui:style type="com.unnison.demo.client.jsonserializer.JsonSerializerPresenter.CustomStyle">
	.style1{
		border: 2px solid black;	
	}
	
	.style2{
		border: 2px solid red;	
	}
</ui:style>

<g:HTMLPanel>
	Id <g:TextBox ui:field="id"></g:TextBox><br/><br/>
	Name <g:TextBox ui:field="name"></g:TextBox> <br/><br/>
	Birthday <d:DateBox ui:field="birthday"></d:DateBox><br/><br/>
	Address <g:TextBox ui:field="address"></g:TextBox><br/><br/><br/>
	
	Json <br/><g:TextArea ui:field="json" width="300px" height="300px"></g:TextArea>
</g:HTMLPanel>
</ui:UiBinder>

Presenter:

@RunAsync
public class JsonSerializerPresenter extends
    AbstractPresenter<JsonSerializerPresenter, JsonSerializerView, JsonSerializerBinder> implements
    Place<Person> {

    public static interface CustomStyle extends CssResource {
        String style1();
        
        String style2();
    }
    
    @ViewField
    CustomStyle style;

     ....
}

Just like you did in UiBinder, but don't bind Widgets, only interfaces

    @ViewField
    HasText name; // TextBox

Caveat

The field injection mechanism (String name$value as parameter of a @ViewHandler method) was designed to make your unit-tests easier.

This new feature require a more complex mocking system.

I decided to include this feature cause a found a way to automatically mock this kind of interfaces. The new mocking utility will work automatically with any GuitTest.

Mocking plan

The idea:

public interface HasText {

  String getText();

  void setText(String text);
}

Will generate a mock equivalent to this:

public class HasTextImpl implements HasText {
   
   String text;

  public String getText() {
      return text;
   }

  public void setText(String text) {
     this.text = text;
  }
}

Using a Proxy.

Interface emulation

Sometimes we will love to have a few extra interfaces. Guit will be able to emulate them.

Example:

@ViewField
HasStyleName name; // TextBox

You will have to create the interface:

public interface HasStyleName {
    void setStyleName(String styleName);

    String getStyleName();
}

And Guit will wrap your widget in this interface. Calling those methods for you.

Multiple bindings of the same field

What if you have a TextBox and you want to bind it as a HasText and also as a HasStyleName?

    @ViewField
    HasText name; // TextBox

    @ViewField("name")
    HasStyleName nameStyle; // TextBox

Why not to have a TextBoxI with all the TextBox methods?

I think that is a great idea. But you won't see that inside the framework. I will prefer to see it inside gwt, it will even benefit the regular uiBinder usage.

Custom wrappers

Guit have a mechanism to make custom ui:fields wrappers. By using and interface you are as mockeable as before, and if you keep it as a Pojo it will even work on your tests.

The implementation is another story. There you can do whatever you want and its not necessary to say that it won't be used in tests, only in development mode.

Here is how you can access the style of a widget (this is not framework code, you can write your own ViewAccesors):

Interface:

@Implementation(StylerImpl.class)
public interface Styler extends ViewAccesor {
    
    void setBackgroundColor(String color);
    
    String getBackgroundColor();

    void setFontSizePx(double fontSize);
    
    void setVisibility(Visibility visibility);
}

Implementation:

public class StylerImpl implements Styler {

    Element element;
    
    @Override
    public void setTarget(Object target) {
        if (target instanceof Widget) {
            element = ((Widget)target).getElement();
        } else if (target instanceof Element) {
            element = (Element) target;
        } else {
            assert false : "Invalid element type '" + target.getClass().getName() + "' for Styler";
        }
    }

    @Override
    public void setBackgroundColor(String color) {
        element.getStyle().setBackgroundColor(color);
    }

    @Override
    public String getBackgroundColor() {
        return element.getStyle().getBackgroundColor();
    }
    
    @Override
    public void setFontSizePx(double value) {
        element.getStyle().setFontSize(value, Unit.PX);
    }

    @Override
    public void setVisibility(Visibility visibility) {
        element.getStyle().setVisibility(visibility);
    }
}

Testing

In your GuitTest's don't worry about mocking the ViewField's, Guit will mock them for you and set the mock into the fields automatically when you get a presenter.

Powered by Google Project Hosting