|
ViewField
Overview of the new @ViewField annotation
IntroductionViewField 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
New approach
The new approach force you to write better codeWhen 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 presenterNow 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; // TextBoxCaveatThe 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 planThe 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 emulationSometimes 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 fieldWhat 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; // TextBoxWhy 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 wrappersGuit 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);
}
}TestingIn 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. | |