Cómo combinar múltiples widgets basados ??en UiBinder?
Pregunta
I necesita insertar una [número de] widgets basados ??en UiBinder en otra, en un punto particular. El widget insertado tiene un diseño algo complicado, por lo que estoy tratando de definirlo en HTML.
referencePanel.add (...) falla con java.lang.IllegalStateException: los padres de este widget de no implementa HasWidgets. No sé a qué widget de los padres que es infeliz - innerPanel o referencePanel.
Si se añade el objeto ReferenceUI a RootPanel, y después se añade a la parte inferior de la página. Pero si se añade a RootPanel primera, entonces hay un Código JavaScriptException 3 (HIERARCHY_REQUEST_ERR) cuando se añade a referencePanel.
¿Alguna sugerencia?
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>
Solución
Voy a empezar con la revisión, a continuación, pasar a explicar después.
Fijación del problema
La forma más fácil de solucionar este problema es el siguiente. Aquí está el código:
HTMLPanel innerPanel = reference.getRefPanel();
innerPanel.getElement().setId(HTMLPanel.createUniqueId());
referencePanel.add(innerPanel);
Reemplazar que con el siguiente código. Tenga en cuenta que sólo la última línea ha cambiado.
HTMLPanel innerPanel = reference.getRefPanel();
innerPanel.getElement().setId(HTMLPanel.createUniqueId());
referencePanel.add(reference);
De esta manera, va a agregar el objeto Composite
. No habrá ninguna diferencia para el usuario, ya que el HTMLPanel
(su innerPanel
) se adjuntará directamente en el documento.
Algunos antecedentes
Adición de un widget a un panel complejo
Al agregar un widget a un panel complejo (un panel que tiene más de un widget hijo), cuatro cosas suceden uno tras otro:
- El siguiente widget se le dice a sustraerse a su matriz actual
- El panel añade el widget a su lista de niños
- El panel añade el widget al documento
- El siguiente widget se cuenta para fijar el panel como su nuevo padre
Contar una widget para sustraerse a su matriz
Cuando un niño se le dice a sustraerse a su padre, uno de las siguientes situaciones:
- Si el widget no tiene un padre, que no hace nada
- Si el widget es el hijo de un panel de instrumentos que
HasWidgets
, dice el widget del panel para eliminar ese widget - De lo contrario, el widget lanza
IllegalStateException
con el mensaje "padre de este widget de no implementa HasWidgets
Compuesto de widgets
Cuando se llama a initWidget(Widget)
, padre del widget se establece en el objeto Composite
.
El problema
Cuando intenta agregar innerPanel
, se dijo a sustraerse a su matriz actual. innerPanel
es en realidad la raíz de la plantilla UiBinder. Su padre es un objeto Composite
(en concreto, ReferenceUI
). El resultado es la excepción que son lanzados, como Composite
no implementa HasWidgets
y no apoya la eliminación de su artilugio.
Otros consejos
Por desgracia, IIRC no se puede establecer un ID para un widget en el código de UiBinder (creo que se puede sólo para HTML normal). Lo que se quedan con el establecimiento de la correcta identificación a través de someWidget.getElement().setId()
(como lo hizo en el código anterior).
Lo que falta ahora, creo, es un marcador de posición (un div, palmo, lo que sea) con el codigo correcto en el HTMLPanel referencePanel
, así:
<g:HTMLPanel ui:field="referencePanel">
<div ui:field="referencePanelInner" />
</g:HTMLPanel>
Y en AppUIDemo
:
@UiField
DivElement referencePanelInner;
// ...
public AppUIDemo() {
// ...
referencePanelInner.setId(reference.getRefPanelId());
}
... a menos que desee añadir el ReferenceUI directamente en referencePanel
- en cuyo caso sólo debe utilizar un FlowPanel
:)
PS:. En mi humilde opinión, se debe generar y establecer el identificador único para el ReferenceUI.refPanel
de la clase ReferenceUI
(y exponer el id) - de ese modo no se juega con "internos" del widget de algunos widgets externos
OK, creo que lo tengo. La excepción IllegalStateException
se produce porque está agregando ReferenceUI.refPanel
(o ReferenceUI.referencePanel
, no sé que es el nombre actual) a AppUIDemo
(ReferenceUI
obviamente no implementa HasWidgets
) - cuando debería ser la adición de todo el ReferenceUI reference
compuesta :) I no se sabe por qué lo hizo de esta manera en el primer lugar (tal vez por todo el lío con los ID), pero el código debe ser algo como esto:
public AppUIDemo() {
initWidget(uiBinder.createAndBindUi(this));
ReferenceUI reference = new ReferenceUI(refUIBinder);
referencePanel.add(reference);
}