如何组合多个基于uiBinder的小部件?

发布于 2024-09-01 20:06:10 字数 5699 浏览 4 评论 0原文

我需要将[数量]个基于 uiBinder 的小部件插入到另一个小部件中的特定位置。插入的小部件的布局有些复杂,因此我尝试在 HTML 中定义它。

ReferencePanel.add(...) 失败并出现 java.lang.IllegalStateException:此小部件的父级未实现 HasWidgets。不知道它对哪个小部件的父级不满意——innerPanel 或referencePanel。

如果ReferenceUI对象被添加到RootPanel,然后它被添加到页面的底部。但如果先添加到RootPanel, 然后添加到referencePanel 时会出现JavaScriptException Code 3 (HIERARCHY_REQUEST_ERR)。

有什么建议吗?

public class AppUIDemo extends Composite {
    @UiTemplate("AppUIDemo.ui.xml")
    interface AppUIDemoUiBinder extends UiBinder<Widget, AppUIDemo> {
    }

    @UiTemplate("ReferenceUI.ui.xml")
    interface ReferenceUIUiBinder extends
            UiBinder<Widget, ReferenceUI> {
    }

    private static AppUIDemoUiBinder uiBinder = GWT
            .create(AppUIDemoUiBinder.class);
    private static ReferenceUIUiBinder refUIBinder = GWT
            .create(ReferenceUIUiBinder.class);

    @UiField
    FlowPanel referencePanel;

    public AppUIDemo() {
            initWidget(uiBinder.createAndBindUi(this));
            ReferenceUI reference = new ReferenceUI(refUIBinder);

            HTMLPanel innerPanel = reference.getRefPanel();
            innerPanel.getElement().setId(HTMLPanel.createUniqueId());
            referencePanel.add(innerPanel);
        }
}

 

public class ReferenceUI extends Composite {

    interface ReferenceUIUiBinder extends
            UiBinder<Widget,ReferenceUI> {
    }

    private static ReferenceUIUiBinder uiBinder = GWT
            .create(ReferenceUIUiBinder.class);


    @UiField
HTMLPanel refPanel;

    public ReferenceUI() {
        initWidget(uiBinder.createAndBindUi(this));
    }

    public CreditReferenceUI(final UiBinder<Widget, CreditReferenceUI> binder) {
        initWidget(binder.createAndBindUi(this));
    }

    public HTMLPanel getRefPanel() {
        return refPanel;
    }
}

参考UI.ui.xml

<!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:gwittir="urn:import:com.totsp.gwittir.client.ui">
            <g:HTMLPanel ui:field="referencePanel">
            <table>
            <tr>
                <td>
                    <b>First Name</b></td>
                <td>
                    <b>Last Name</b></td>
                <td>
                    <b>Phone</b></td>
                <td>
                    <b>Fax</b></td>
            </tr>
            <tr>
                <td>
                    <gwittir:TextBox ui:field="referenceFirstName" styleName="input"/></td>
                <td>
                    <gwittir:TextBox ui:field="referenceLastName" styleName="input"/></td>
                <td>
                    <table><tr>
            <td> ( </td> <td>
                <gwittir:TextBox ui:field="referencePhoneAreaCode" styleName="input" maxLength="3"/></td>
                <td> ) </td> <td>
                <gwittir:TextBox ui:field="referencePhoneNumber" styleName="input" maxLength="7"/></td>
            <td> # </td> <td>
                <gwittir:TextBox ui:field="referencePhoneExtension" styleName="input" maxLength="25"/></td>
        </tr></table></td>
                <td>
                     <table><tr>
            <td> ( </td> <td>
                <gwittir:TextBox ui:field="referenceFaxAreaCode" styleName="input" maxLength="3"/></td>
                <td> ) </td> <td>
                <gwittir:TextBox ui:field="referenceFaxNumber" styleName="input" maxLength="7"/></td>
        </tr></table></td>
                </tr>
            <tr>
                <td style="text-align:right">
                    <b>Address: </b> Street</td>
                <td>
                    <gwittir:TextBox ui:field="referenceStreet" styleName="input"/></td>
                <td colspan="2" style="width:50%">
                    <table><tr><td>   City</td>
                    <td><gwittir:TextBox ui:field="referenceCity" styleName="input" maxLength="25"/></td>
                    <td> State </td>
                    <td class="state"><gwittir:TextBox ui:field="referenceState" styleName="input" maxLength="2"/></td>
                    <td> ZIP</td>
                    <td><gwittir:TextBox ui:field="referenceZIP" styleName="input" maxLength="9"/></td></tr></table>
                </td>
            </tr>
        </table>
    </g:HTMLPanel>
</ui:UiBinder>

AppUIDemo.ui.xml

<!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:gwittir="urn:import:com.totsp.gwittir.client.ui">
    <g:HTMLPanel ui:field="basePanel">
        <!--  <div id="MainContent">  -->
        <p><h2><b>Application Demo</b></h2></p>

        <g:FlowPanel ui:field="referencePanel">
        </g:FlowPanel>
    </g:HTMLPanel>
</ui:UiBinder>

I need to insert a [number of] uiBinder-based widgets into another one, at a particular spot. The inserted widget has a somewhat complicated layout, so I am trying to define it in HTML.

referencePanel.add(...) fails with java.lang.IllegalStateException: This widget's parent does not implement HasWidgets. Don't know which widget's parent it's unhappy about - innerPanel or referencePanel.

If the ReferenceUI object is added to RootPanel, and then it's added to the bottom of the page. But if it's added to RootPanel first,
then there is a JavaScriptException Code 3 (HIERARCHY_REQUEST_ERR) when added to referencePanel.

Any suggestions?

public class AppUIDemo extends Composite {
    @UiTemplate("AppUIDemo.ui.xml")
    interface AppUIDemoUiBinder extends UiBinder<Widget, AppUIDemo> {
    }

    @UiTemplate("ReferenceUI.ui.xml")
    interface ReferenceUIUiBinder extends
            UiBinder<Widget, ReferenceUI> {
    }

    private static AppUIDemoUiBinder uiBinder = GWT
            .create(AppUIDemoUiBinder.class);
    private static ReferenceUIUiBinder refUIBinder = GWT
            .create(ReferenceUIUiBinder.class);

    @UiField
    FlowPanel referencePanel;

    public AppUIDemo() {
            initWidget(uiBinder.createAndBindUi(this));
            ReferenceUI reference = new ReferenceUI(refUIBinder);

            HTMLPanel innerPanel = reference.getRefPanel();
            innerPanel.getElement().setId(HTMLPanel.createUniqueId());
            referencePanel.add(innerPanel);
        }
}

 

public class ReferenceUI extends Composite {

    interface ReferenceUIUiBinder extends
            UiBinder<Widget,ReferenceUI> {
    }

    private static ReferenceUIUiBinder uiBinder = GWT
            .create(ReferenceUIUiBinder.class);


    @UiField
HTMLPanel refPanel;

    public ReferenceUI() {
        initWidget(uiBinder.createAndBindUi(this));
    }

    public CreditReferenceUI(final UiBinder<Widget, CreditReferenceUI> binder) {
        initWidget(binder.createAndBindUi(this));
    }

    public HTMLPanel getRefPanel() {
        return refPanel;
    }
}

ReferenceUI.ui.xml

<!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:gwittir="urn:import:com.totsp.gwittir.client.ui">
            <g:HTMLPanel ui:field="referencePanel">
            <table>
            <tr>
                <td>
                    <b>First Name</b></td>
                <td>
                    <b>Last Name</b></td>
                <td>
                    <b>Phone</b></td>
                <td>
                    <b>Fax</b></td>
            </tr>
            <tr>
                <td>
                    <gwittir:TextBox ui:field="referenceFirstName" styleName="input"/></td>
                <td>
                    <gwittir:TextBox ui:field="referenceLastName" styleName="input"/></td>
                <td>
                    <table><tr>
            <td> ( </td> <td>
                <gwittir:TextBox ui:field="referencePhoneAreaCode" styleName="input" maxLength="3"/></td>
                <td> ) </td> <td>
                <gwittir:TextBox ui:field="referencePhoneNumber" styleName="input" maxLength="7"/></td>
            <td> # </td> <td>
                <gwittir:TextBox ui:field="referencePhoneExtension" styleName="input" maxLength="25"/></td>
        </tr></table></td>
                <td>
                     <table><tr>
            <td> ( </td> <td>
                <gwittir:TextBox ui:field="referenceFaxAreaCode" styleName="input" maxLength="3"/></td>
                <td> ) </td> <td>
                <gwittir:TextBox ui:field="referenceFaxNumber" styleName="input" maxLength="7"/></td>
        </tr></table></td>
                </tr>
            <tr>
                <td style="text-align:right">
                    <b>Address: </b> Street</td>
                <td>
                    <gwittir:TextBox ui:field="referenceStreet" styleName="input"/></td>
                <td colspan="2" style="width:50%">
                    <table><tr><td>   City</td>
                    <td><gwittir:TextBox ui:field="referenceCity" styleName="input" maxLength="25"/></td>
                    <td> State </td>
                    <td class="state"><gwittir:TextBox ui:field="referenceState" styleName="input" maxLength="2"/></td>
                    <td> ZIP</td>
                    <td><gwittir:TextBox ui:field="referenceZIP" styleName="input" maxLength="9"/></td></tr></table>
                </td>
            </tr>
        </table>
    </g:HTMLPanel>
</ui:UiBinder>

AppUIDemo.ui.xml

<!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:gwittir="urn:import:com.totsp.gwittir.client.ui">
    <g:HTMLPanel ui:field="basePanel">
        <!--  <div id="MainContent">  -->
        <p><h2><b>Application Demo</b></h2></p>

        <g:FlowPanel ui:field="referencePanel">
        </g:FlowPanel>
    </g:HTMLPanel>
</ui:UiBinder>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冰葑 2024-09-08 20:06:10

我将从修复开始,然后继续解释。

解决问题

解决此问题的最简单方法如下。这是您的代码:

HTMLPanel innerPanel = reference.getRefPanel();
innerPanel.getElement().setId(HTMLPanel.createUniqueId());
referencePanel.add(innerPanel);

将其替换为以下代码。请注意,只有最后一行发生了变化。

HTMLPanel innerPanel = reference.getRefPanel();
innerPanel.getElement().setId(HTMLPanel.createUniqueId());
referencePanel.add(reference);

这样,您就可以添加 Composite 对象。对于用户来说没有什么区别,因为 HTMLPanel(您的 innerPanel)将直接附加到文档中。


一些背景

将小部件添加到复杂面板

当您将小部件添加到复杂面板(包含多个子小部件的面板)时,会依次发生四件事:

  1. 小部件被告知将其自身从其面板中删除。当前父级
  2. 面板将小部件添加到其子级列表中
  3. 面板将小部件添加到文档中
  4. 小部件被告知将面板设置为其新父级

告诉小部件从其父级中删除自身

当子级被告知时要将其自身从其父级中删除,会发生以下情况之一:

  • 如果小部件没有父级,则不会执行任何操作
  • 如果小部件是实现 HasWidgets 的面板的子级,则小部件会告诉面板删除该小部件
  • 否则,该小部件将抛出 IllegalStateException 并显示消息“此小部件的父级未实现 HasWidgets

复合小部件

调用 initWidget(Widget) 时,小部件的父级设置为 Composite 对象。

问题

当您尝试添加 innerPanel 时,它被告知将其自身从当前父级中删除。 innerPanel 实际上是 UiBinder 模板的根。它的父级是一个Composite 对象(具体来说,ReferenceUI)。这会导致抛出异常,因为 Composite 未实现 HasWidgets 并且不支持删除其小部件。

I'll start with the fix, then move on to explain it afterward.

Fixing the problem

The easiest way to fix this problem is as follows. Here's your code:

HTMLPanel innerPanel = reference.getRefPanel();
innerPanel.getElement().setId(HTMLPanel.createUniqueId());
referencePanel.add(innerPanel);

Replace that with the following code. Note that only the last line has changed.

HTMLPanel innerPanel = reference.getRefPanel();
innerPanel.getElement().setId(HTMLPanel.createUniqueId());
referencePanel.add(reference);

This way, you are adding the Composite object. There will be no difference to the user, as the HTMLPanel (your innerPanel) will be directly attached into the document.


Some background

Adding a widget to a complex panel

When you add a widget to a complex panel (a panel that holds more than one child widget), four things happen one after the other:

  1. The widget is told to remove itself from its current parent
  2. The panel adds the widget to its list of children
  3. The panel adds the widget to the document
  4. The widget is told to set the panel as its new parent

Telling a widget to remove itself from its parent

When a child is told to remove itself from its parent, one of the following occurs:

  • If the widget does not have a parent, it does nothing
  • If the widget is the child of a panel that implements HasWidgets, the widget tells the panel to remove that widget
  • Otherwise, the widget throws IllegalStateException with message "This widget's parent does not implement HasWidgets

Composite widgets

When calling initWidget(Widget), the widget's parent is set to the Composite object.

The problem

When you try to add innerPanel, it is told to remove itself from its current parent. innerPanel is actually the root of your UiBinder template. Its parent is a Composite object (specifically, ReferenceUI). This results in the exception being thrown, as Composite does not implement HasWidgets and does not support removing its widget.

暗恋未遂 2024-09-08 20:06:10

不幸的是,IIRC 你不能在 UiBinder 的代码中为 Widget 设置 id(我认为你只能为普通的 HTML)。因此,您只能通过 someWidget.getElement().setId() 设置正确的 id(就像您在上面的代码中所做的那样)。

我认为,现在缺少的是 HTMLPanel referencePanel 中具有正确 id 的占位符(div、span 等),如下所示:

<g:HTMLPanel ui:field="referencePanel">
    <div ui:field="referencePanelInner" />
</g:HTMLPanel>

AppUIDemo 中:

@UiField
DivElement referencePanelInner;
// ...
public AppUIDemo() {
    // ...
    referencePanelInner.setId(reference.getRefPanelId());
}

..除非您想将 ReferenceUI 直接添加到 referencePanel 中 - 在这种情况下您应该只使用 FlowPanel :)

PS:恕我直言,您应该生成并设置唯一 id来自 ReferenceUI 类的 ReferenceUI.refPanel (并公开 id) - 这样您就不会从某些外部小部件中弄乱小部件的“内部结构”。


好吧,我想我明白了。抛出 IllegalStateException 异常,因为您正在添加 ReferenceUI.refPanel (或 ReferenceUI.referencePanel,我不知道哪个是当前名称)到 AppUIDemoReferenceUI 显然没有实现 HasWidgets) - 当您应该添加整个 ReferenceUI 参考复合:)我不知道你为什么这样做(也许是因为 ids 混乱),但代码应该看起来像这样:

public AppUIDemo() {
        initWidget(uiBinder.createAndBindUi(this));
        ReferenceUI reference = new ReferenceUI(refUIBinder);

        referencePanel.add(reference);
    }

Unfortunately, IIRC you can't set an id for a Widget in UiBinder's code (I think you can only for normal HTML). So you are left with setting the correct id via someWidget.getElement().setId() (like you did in the code above).

What's missing now, I think, is a placeholder (a div, span, whatever) with the correct id in the HTMLPanel referencePanel, like so:

<g:HTMLPanel ui:field="referencePanel">
    <div ui:field="referencePanelInner" />
</g:HTMLPanel>

And in AppUIDemo:

@UiField
DivElement referencePanelInner;
// ...
public AppUIDemo() {
    // ...
    referencePanelInner.setId(reference.getRefPanelId());
}

...unless you want to add the ReferenceUI directly into referencePanel - in which case you should just use a FlowPanel :)

PS: IMHO, you should generate and set the unique id for the ReferenceUI.refPanel from the ReferenceUI class (and expose the id) - that way you don't mess with the widget's "internals" from some external widgets.


OK, I think I got it. The IllegalStateException exception is being thrown because you are adding ReferenceUI.refPanel (or ReferenceUI.referencePanel, I don't know which is the current name) to AppUIDemo (ReferenceUI obviously doesn't implement HasWidgets) - when you should be adding the whole ReferenceUI reference composite :) I don't know why you did it this way in the first place (maybe because of the whole mess with the ids), but the code should look something like this:

public AppUIDemo() {
        initWidget(uiBinder.createAndBindUi(this));
        ReferenceUI reference = new ReferenceUI(refUIBinder);

        referencePanel.add(reference);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文