GWT: ‘A widget that has an existing parent widget may not be added to the detach list’

GWTのクライアントコンソールに以下のようなエラーが表示された。

Error: java.lang.AssertionError: A widget that has an existing parent widget may not be added to the detach list

GWT: ‘A widget that has an existing parent widget may not be added to the detach list’に説明してくれてる人がいる。

これは既存のDOM要素をGWTウィジェットでラップする時に発生する。例えばButton.wrap(…)、HTML.wrap(…)等など。

分析

GWTでは不可能のようだ。ドキュメントのDOMについて、その親のいずれかが既にGWTウイジェットでラップされている場合、その子をラップすることは。例えば、

<div style=”display: none” id=“outer_element”>
  <form class=“well”>
    <label><i class=“icon-exclamation-sign”></i> An error occurred while requesting your API key.</label>
    <div id=“inner_element”>No error message.</div>
  </form>
</div>

この状態で以下を行う。

HTML errorForm = HTML.wrap(DOM.getElementById(“outer_element”));
Label errorMessage = Label.wrap(DOM.getElementById(“inner_element”));

すると、inner_elementの時点でエラーが発生する。outer_elementが既にラップされているからだ。

解決策

単純に内から外にラップしていけば、エラーを避けることができる。上の場合は、単純にラップの順序を逆転する。

Label errorMessage = Label.wrap(DOM.getElementById(“inner_element”));
HTML errorForm = HTML.wrap(DOM.getElementById(“outer_element”));

参照

より詳細な分析

どのElementでも同じなのだが、例えばAnchor.wrap(…)を呼び出す。これは以下のコードになっている。

  public static Anchor wrap(Element element) {
    // Assert that the element is attached.
    assert Document.get().getBody().isOrHasChild(element);

    Anchor anchor = new Anchor(element);

    // Mark it attached and remember it for cleanup.
    anchor.onAttach();
    RootPanel.detachOnWindowClose(anchor);

    return anchor;
  }

エラーは最後のRootPanel.detachOnWindowClose(anchor)で発生している。

RootPanel.detachOnWindowClose(…)の機能としては、detach Listというもののメンテナンスなのだが、これはgwt detach listに説明がある。