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>
I'll start with the fix, then move on to explain it afterward.
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.
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:
When a child is told to remove itself from its parent, one of the following occurs:
HasWidgets
, the widget tells the panel to remove that widgetIllegalStateException
with message "This widget's parent does not implement HasWidgetsWhen calling initWidget(Widget)
, the widget's parent is set to the Composite
object.
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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With