当小部件非常简单时,我只在 UiBinder 上使用小部件。例如,我只是将两个小部件添加到Panel
. 如果涉及任何 CSS,我总是使用 UiBinder,因为它更容易使用样式。
小部件不会被翻译成 UiBinder XML。所有 GWT 代码都变成了添加和操作 DOM 元素的 JavaScript,因此所有内容都被转换为可执行语言,而不是模板系统。
我写了很多 UiBinder 片段。我尝试遵循您可以在网络上找到的关于抽象和组合的良好规则。
如果您有任何重要的逻辑,MVP 模式是必须的,因为使用 JUnit 测试无 GWT 的演示者非常快速和容易,而 GWT 测试的开销要大得多,而且速度要慢得多。
我喜欢在 CSS 文件中保留尽可能多的样式,因为分离关注点是一种标准的好习惯,您可以压缩和捆绑您的 CSS 文件,以及许多其他原因,这些原因与您在普通 HTML 页面中放置 CSS 的原因相同在单独的文件中,而不是直接在页面上。
我从不使用 GWT 设计器。我总是更喜欢干净的代码,而不是通常由任何 UI 代码生成器吐出的疯狂垃圾。
99% 的情况下,我的小部件会扩展Composite
,因为要么我正在使用 UiBinder,要么我正在向Panel
. 即使我只有一个小部件,我发现扩展Composite
并将我的一个小部件添加到SimplePanel
. 我很少扩展Widget
,因为你必须调用来Document.get().createFooElement()
创建一个 DOM Element
,但我通常发现Widget
s,可以添加到Panel
s,比Element
s更容易和更安全
我如何构建小部件
每个小部件都实现了一个扩展接口IsWidget
。每个想要使用的人都Widget
应该依赖于接口,而不是底层类。这提供了一个单一的、无 JSNI 的抽象。
如果小部件非常简单,我将有一个扩展Composite
和实现接口的类。小部件将非常简单并向 a 添加一些项目,Panel
或者它将使用 UiBinder。
如果小部件有我想测试的重要逻辑,我使用 MVP 模式。将有一个实现小部件的“公共”接口的演示者类,一个扩展IsWidget
演示者所依赖的视图接口,以及一个实现视图接口的视图小部件。
为小部件提供单个“公共”接口的一个好处是,如果逻辑变得复杂,您可以从使用单个Composite
类实现接口更改为使用 MVP,并且使用小部件的人根本不需要更改。
我使用 Gin 将所有接口和实现连接在一起。
例子
这最好用一些代码来解释。假设我有一个要在多个页面上使用的图表,所以我决定为它制作一个可重复使用的小部件。在显示之前处理 RPC 响应有一些重要的逻辑,所以我想对它进行彻底的单元测试。我会用这样的东西:
public interface FinancialChart extends IsWidget {
void setTickerSymbol(String tickerSymbol);
}
class FinancialChartPresenter extends Composite implements FinancialChart {
private final FinancialChartView view;
private final DataServiceAsync service;
@Inject(FinancialChartView view, DataServiceAsync service) {
this.view = view;
this.service = service;
}
@Override public Widget asWidget() {
return view.asWidget();
}
@Override public void setTickerSymbol(String tickerSymbol) {
service.getData(tickerSymbol, new AsyncCallback<FinancialData>() {
@Override public void onFailure(Throwable t) {
// handle error
}
@Override public void onSuccess(FinancialData data) {
SimpleData simpleData = // do some parsing with presentation-specific
// logic, e.g. make dramatic increases or decreases in price have a
// a different color so they stand out. End up with something simple
// that's essentially some (x, y) points that the dumb view can plot
// along with a label and color for each point.
view.drawGraph(simpleData);
}
}
}
interface FinancialChartView extends IsWidget {
void drawGraph(SimpleData simpleData);
}
class FinancialChartWidget extends Composite implements FinancialChartView {
@Override public void drawGraph(SimpleData simpleData) {
// plot the points on a chart. set labels. etc.
}
}
class SomethingWithFinancialChartWidget extends Composite
implements SomethingWithFinancialChart {
interface Binder extends UiBinder<Widget, SomethingWithFinancialChartWidget> {}
@UiField(provided = true) final FinancialChart chart;
@Inject SomethingWithFinancialChartWidget(Binder binder, FinancialChart chart) {
this.chart = chart;
initWidget(binder.createAndBindUi(this));
}
}
// In SomethingWithFinancialChartWidget.ui.xml
<ui:HTMLPanel>
<!-- lots of stuff -->
<mynamespace:FinancialChart ui:field="chart" />
<!-- lots more stuff -->
</ui:HTMLPanel>
class MyPackagesGinModule extends AbstractGinModule {
@Override protected void configure() {
bind(FinancialChart.class).to(FinancialChartPresenter.class);
bind(FinancialChartView.class).to(FinancialChartWidget.class);
}
}
这允许我为 JSNI 编写非常简单、彻底和快速的 JUnit 测试,FinancialViewPresenter
因为它没有需要 JSNI 的 GWT 依赖项,它必须作为慢得多的 GWT 测试用例的一部分在浏览器中运行。您可以创建一个模拟FinancialChartView
.
这里要注意的一件事是,由于SomethingWithFinancialChartWidget
依赖于 interface FinancialChart
,它不能实例化该对象,因为它只是一个接口。这就是为什么在 .java 代码中chart
设置的原因。Gin 设置从接口到具体类的绑定,以便它可以为 的构造函数提供实现,然后设置为 UiBinder 提供所需的对象。@UiField(provided = true)
SomethingWithFinancialChartWidget
FinancialChart
@Inject
SomethingWithFinancialChartWidget
this.chart
FinancialChart
在 MVP 中为所有接口和实现创建了许多文件,但抽象是绝对值得的,因为它们使演示者的单元测试变得容易,并允许您更改在此示例中的顶级接口的实现方式,例如,从单个Composite
类更改为 MVP,无需客户端更改。
我确信有一些实现细节可能不是很清楚,或者我掩盖了一些事情,例如 GWT 测试,所以请发表评论,我可以编辑我的答案以更新和澄清。