GWT UiBinder - 在呈现模板后执行 JS?

GWT UiBinder - Execute JS after template is rendered?

我的 UiBinder 模板中有一个 div,ID 为 "test",我想在其上执行一些 jQuery 操作。如果我在 UiBinder java class 末尾执行本地方法,java 脚本调用失败,因为 div 似乎还不存在:

public MyWidget() {
    initWidget(uiBinder.createAndBindUi(this));
    AppUtils.initializeSomething();
}

解决方法是使用计时器并稍微延迟我的 JS 代码。这可行,但在我看来非常难看。如果 UiBinder 模板已完全加载,是否有任何干净的解决方案来执行我的 java 脚本?

public MyWidget() {
    initWidget(uiBinder.createAndBindUi(this));

    new Timer(){
        @Override
        public void run() {
            AppUtils.initializeSomething();
        };
    }.schedule(1000);

}

原生方法:

public static native void initializeSomething()/*-{

     $wnd.$('#test').doSomeJsStuff();
}-*/;
public MyWidget() {
    initWidget(uiBinder.createAndBindUi(this));
    AppUtils.initializeSomething();
}

由于这只是构造函数,此时 DOM 元素全部存在,但尚未附加,因此任何试图查找附加的 dom 元素的代码都将失败(因为例如,jQuery 将尝试执行 document.getElementById 来计算 $('#test'),这将失败,因为小部件尚未附加到文档)。

相反,您有两个基本选项:

  • 运行 立即初始化,但实际上传入了 dom 元素而不是试图找到它(并且失败),或者
  • 等待 dom 元素实际附加后再尝试初始化。

运行初始化只是已经知道要修改哪个元素的问题。由于您使用的是 uiBinder,因此您可以在 Java class 中创建一个 Element 字段,并要求将其分配给某个设置了 ui:field 属性的元素同名。这就像你当前如何将元素的 ID 设置为 "test",然后期望通过 `$('#test') 找到它。像这样:

在您的 .ui.xml 文件中:

<g:HTMLPanel>
  <div ui:field="testElement">...</div>
  ...
</g:HTMLPanel>

在你的.java class:

@UiField Element testElement;

然后在你的JSNI方法中你可以传入元素或者在调用的时候引用它。这是您的 initializeSomething 和构造函数的样子:

public MyWidget() {
    initWidget(uiBinder.createAndBindUi(this));
    AppUtils.initializeSomething(this.testElement);
}
public static native void initializeSomething(Element elt)/*-{
    //jQuery lets you wrap a dom element instead of finding it,
    //and we already know where it is
    $wnd.$(elt).doSomeJsStuff();
}-*/;

接下来,等到附加到DOM到运行doSomeJsStuff()——这两个简单的方法。在原始 JS 中,您会以第一种方式执行此操作,但我建议这对 JS 来说是非常错误的。所以这是第一个错误的方法:

//make the elements
MyWidget widget = new MyWidget();
//attach them to the dom (note that if you attach to a different
//                        parent, that parent too must be attached
//                        and so on).
RootPanel.get().add(widget);
//now that we are attached, run the setup code!
widget.doSomeJsStuff();

这显然很愚蠢 - 我们正在编写 class,而 class 应该足够聪明,可以照顾好自己!

让我们再试一次,这次将 class 指示为 运行 doSomeJsStuff() 已附加。有两种方法可以完成这项工作,onAttachonLoad - 没有关于 doSomeJsStuff 做什么的具体细节,我不知道你需要什么,但是 onLoad是更容易使用的。

请务必注意,每次附加小部件时都会调用这些方法,而不仅仅是第一次。如果这对您很重要,您可能需要检查是否尚未附加小部件。如果没有,我们只需要这个:

@Override
protected void onLoad() {
  doSomeJsStuff();
}