http://www.tessell.org
public void testNameIsRequired() {
v.submitButton().click();
assertThat(v.nameErrors(), hasError("Required"));
}
public void testServerResponse() {
v.submitButton().click();
doSubmitResult("some error from server");
assertThat(v.nameErrors(), hasError("some error..."));
}
1. Change somePage.ui.xml
<gwt:TextBox ui:field="foo" />
2. Change IsSomePageView
public interface IsSomePageView {
HasText getFoo();
}
3. Change GwtSomePageImpl
@UiField
TextBox foo;
public HasText getFoo() {
return this.foo;
}
4. Update presenter
1. Change somePage.ui.xml
<gwt:TextBox ui:field="foo" />
2. Change IsSomePageView
interface
public interface IsSomePageView {
IsTextBox getFoo();
}
3. Change GwtSomePageView
implementation
@UiField
GwtTextBox foo;
public IsTextBox getFoo() {
return this.foo;
}
4. Update presenter
Fast, no-boilerplate workflow
Demo
Handler spaghetti code
textBox.addChangeHandler(new ChangeHandler() {
public void onChange() {
model.setName(textBox.getValue());
}
});
model.addChangeHandler(new ChangeHandler() {
public void onChange() {
textBox.setValue(model.getName());
}
});
textBox.addChangeHandler(e -> model.setName(textBox.getValue()));
model.addChangeHandler(e -> textBox.setValue(model.getName()));
Think declaratively
public void onBind() {
binder.bind(model.name).to(view.textBox());
}
With collections
binder.bind(model.children).to(view.panel(), new ListViewFactory() {
public IsWidget create(ChildDto child) {
return new ChildPresenter(child);
}
}));
Conditional logic
binder.when(editing).is(true).set(css.editing()).on(view.li());
On user input
binder.onKeyDown(view.editBox(), KEY_ESCAPE).set(editing).to(false);
Given a DTO:
class ChildDto {
public String name;
public Dollars salary;
}
How would this work?
ChildDto c = new ChildDto();
binder.bind(c.name).to(view.textBox()); // ??
When ChildDto.name changes?
Build models object:
class ChildModel {
public StringProperty name = stringProperty("name");
public DollarsProperty salary = dollarsProperty("salary");
}
Now we can see when name
changes:
ChildModel c = new ChildModel();
c.name.addPropertyChangedHandler() {
public void onChange() {
view.textBox().setValue(c.name.get());
}
}
// Or, shorter way
binder.bind(c.name).to(view.textBox());
Sounds like more boilerplate?
Answer: Code generation
dtonator (http://www.dtonator.org)
ChildDto:
domain: Child
properties: id, name
tessellModel: true
Outputs:
ChildDto dto = new ChildDto(1, "c1");
ChildModel model = new ChildModel(dto);
model.name.get(); // returns c1
Derived values
StringProperty name = stringProperty("name", "n1");
IntegerProperty max = integerProperty("max", 10);
Property<String> shortName = new DerivedProperty<String>() {
public String getDerivedValue() {
return name.get().substring(0, max.get());
}
}
binder.bind(shortName).to(view.shortName());
And soon:
binder.bind(() -> {
name.get().substring(0, max.get())
}).to(view.shortName());
Demo
public class MyPresenter {
@GenPlace(name = "myPlace", async = false)
public static void onRequest(MyPlaceRequest req, AppContext c) {
c.show(new MyPresenter(...));
}
@GenDispatch
public class SaveClientSpec {
@In(1)
ClientDto dto;
@Out(1)
String error;
}
Generates:
async.execute(new SaveClientAction(dto), SaveClientResult r -> {
r.getError(); // logic
});
Out of the box: