GWT: uiBinder-based widget cant be instanced second time - gwt

I created a widget using GWT uiBinder. It works fine, till the moment when I want to instance it second time. After i call constructor second time it returns only raw description from XML and statements in constructor (rootElement.add( new HTML( "panel1" ), leftId );) are just don't work. It throws no error or warning.
Please help
Java class:
public class DashboardLayout extends Composite {
final String leftId = "boxLeft";
final String rightId = "boxRight";
interface DashboardLayoutUiBinder extends UiBinder<HTMLPanel, DashboardLayout> {
}
private static DashboardLayoutUiBinder ourUiBinder = GWT.create( DashboardLayoutUiBinder.class );
#UiField
HTMLPanel htmlPanel;
public DashboardLayout() {
HTMLPanel rootElement = ourUiBinder.createAndBindUi( this );
this.initWidget( rootElement );
rootElement.add( new HTML( "panel1" ), leftId );
rootElement.add( new HTML( "panel2" ), rightId );
}
}
XML descriprion:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
>
<g:HTMLPanel ui:field="htmlPanel">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="40%" id="boxLeft" class="boxContextLeft">
</td>
<td width="60%" id="boxRight" class="boxContextRight">
</td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>

Don't use id="myid" in widgets, as they will be global(which will screw you up) instead of scoped per instantiation of the widget; use ui:field="myid" and then create a corresponding UiField variable in the java class. This will allow the gwt compiler to obfuscate the id's so you don't get collisions between instantiations of the same widget.
DashboardLayout.java
public class DashboardLayout extends Composite {
interface DashboardLayoutUiBinder extends
UiBinder<HTMLPanel, DashboardLayout> {
}
private static DashboardLayoutUiBinder ourUiBinder = GWT
.create(DashboardLayoutUiBinder.class);
#UiField
HTMLPanel htmlPanel;
#UiField
HTML panel1;
#UiField
HTML panel2;
public DashboardLayout() {
HTMLPanel rootElement = ourUiBinder.createAndBindUi(this);
this.initWidget(rootElement);
// do stuff with panel1
panel1.setHTML("<blink>blink</blink>");
// do stuff with panel2
panel2.setHTML("<marquee>marquee</marquee>");
}
}
DashboardLayout.ui.xml
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<g:HTMLPanel ui:field="htmlPanel">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="40%" class="boxContextLeft">
<g:HTML ui:field="panel1"></g:HTML>
</td>
<td width="60%" class="boxContextRight">
<g:HTML ui:field="panel2"></g:HTML>
</td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>

Related

Populating table with apache wicket

I need to dynamically poplulate a table like this. The problem is, it is not a simple table. It has the "rowspan" characteristics.
For a single entry there are multiple fields entries which are being stored in separate rows.
This is a little tricky to populate with Wicket. Any help , advises, suggestions would be great.
This is what the table looks like on the HTML page:
https://jsfiddle.net/sayrandhri/4ktmy6cn/2/
<table>
<tr>
<th>Name</th>
<th>Role</th>
<th>Company</th>
<th>Request</th>
<th>Change</th>
</tr>
<tr>
<td rowspan=2>ABC</td>
<td rowspan=2>User</td>
<td>Y</td>
<td>True</td>
<td>False</td>
</tr>
<tr>
<td>Telecom</td>
<td>True</td>
<td>False</td>
</tr>
<tr>
<td rowspan=3>XYZ </td>
<td rowspan=3>User</td>
<td>O </td>
<td>False</td>
<td>False</td>
</tr>
<tr>
<td>Q</td>
<td>True</td>
<td>True</td>
</tr>
<tr>
<td>R</td>
<td>False</td>
<td>False</td>
</tr>
</table>
You can try following approach:
HTML:
<table>
<tr>
<th>Name</th>
<th>Role</th>
<th>Company</th>
<th>Request</th>
<th>Change</th>
</tr>
<tbody wicket:id="userList">
<tr wicket:id="providerList">
<td wicket:id="userName"></td>
<td wicket:id="roleName"></td>
<td wicket:id="provider"></td>
<td wicket:id="request"></td>
<td wicket:id="change"></td>
</tr>
</tbody>
</table>
Java:
add(new ListView<User>("userList", new PropertyModel<>(this, "users")) {
#Override
protected void populateItem(ListItem<User> listItem) {
final User user = listItem.getModelObject();
listItem.add(new ListView<Provider>("providerList", user.getProviders()) {
#Override
protected void populateItem(ListItem<Provider> listItem) {
final Provider provider = listItem.getModelObject();
Label nameLabel = new Label("userName", user.getName());
Label roleNameLabel = new Label("roleName", user.getRoleName());
listItem.add(nameLabel);
listItem.add(roleNameLabel);
if (user.getProviders().indexOf(provider) == 0) {
AttributeAppender attributeAppender =
AttributeAppender.append("rowspan", user.getProviders().size());
nameLabel.add(attributeAppender);
roleNameLabel.add(attributeAppender);
} else {
nameLabel.setVisible(false);
roleNameLabel.setVisibilityAllowed(false);
}
listItem.add(new Label("provider", provider.getName()));
listItem.add(new Label("request", provider.isRequest()));
listItem.add(new Label("change", provider.isChange()));
}
});
}
});
Be advised that this way user without any providers wont show up on the list at all.
wicket:container solves this problem.
Your html file would look like this:
<wicket:container wicket:id="doubleRow">
<tr>
...
First Row components
...
</tr>
<tr>
...
Second Row components
...
</tr>
</wicket:container>
Assuming you are using a DataTable with Columns:
In the cell that spans several rows you set the rowspan Attribute with a Model. The value in that model will be increased as long as the value of that cell is not changing.
Once you detect a change in the content, you create a new Model with a rowspan value of 1 and assign that again to the current cell.
Something like this might do it, but I cannot test it at the moment:
private IModel<Integer> currentRowSpanModel = new Model<>(0);
...
new AbstractColumn<Object, String>(new Model<>("ColumnWithRowSpan")) {
#Override
public void populateItem(Item<ICellPopulator<Object>> cellItem, String componentId, IModel<Object> rowModel) {
if (/*Content of cell has changed compared to last row*/) {
// write the content to the cell and add the rowspan Attribute
cellItem.add(new Label(componentId, "Content"));
currentRowSpanModel = new Model<>(1);
cellItem.add(AttributeModifier.replace("rowspan", currentRowSpanModel));
} else {
// Hide cell with same content and instead increase rowspan
cellItem.add(new WebMarkupContainer(componentId));
cellItem.setVisible(false);
currentRowSpanModel.setObject(currentRowSpanModel.getObject()+1);
}
}
}

how to hide table using wicket

I want to hide table based on conditions.I got error using this code.please help me to come out this error.
//IN Wicket :
<table class="jtrac jtrac-view" width="100%" wicket:id="request">
<tr>
<td ></td>
<td ></td>
</tr>
</table>
<table class="jtrac jtrac-view" width="100%" wicket:id="response">
<tr >
<td ></td>
<td ></td>
</tr>
</table>
I wrote java code like this.
WebMarkupContainer request = new WebMarkupContainer("request");
WebMarkupContainer response= new WebMarkupContainer("response");
add(request );
add(response);
if(time == null || time.equals("")) {
response.setVisible(false);
add(response);
}else {
request.setVisible(false);
add(request);
}
add(request());
add(response());
private WebMarkupContainer request() {
WebMarkupContainer r = new WebMarkupContainer("request") {
#Override
protected void onConfigure() {
super.onConfigure();
setVisible(StringUtils.isEmpty(time))
}
};
r.setOutputMarkupPlaceholderTag(true);
return r;
}
private WebMarkupContainer response() {
WebMarkupContainer r = new WebMarkupContainer("response") {
#Override
protected void onConfigure() {
super.onConfigure();
setVisible(StringUtils.isNotEmpty(time));
}
};
r.setOutputMarkupPlaceholderTag(true);
return r;
}
StringUtils is from Apache Commons: http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html

How do you do nested Editors in GWT2?

Сould you please give me working example of nested editors ? I've read this document but it didn't help me. In my code I have class Person and Organization.
Organization has field contactPerson of a type Person.
So I created following editor for Person:
public class PersonEditor extends Composite implements Editor<PersonProxy>
{
interface PersonEditorUiBinder extends UiBinder<Widget, PersonEditor>
{
}
private static PersonEditorUiBinder uiBinder = GWT.create(PersonEditorUiBinder.class);
#UiField
ValueBoxEditorDecorator<String> name;
#UiField
ValueBoxEditorDecorator<String> phoneNumber;
#UiField
ValueBoxEditorDecorator<String> email;
#UiField
CaptionPanel captionPanel;
public void setCaptionText(String captionText)
{
captionPanel.setCaptionText(captionText);
}
public PersonEditor()
{
initWidget(uiBinder.createAndBindUi(this));
}
}
its corresponding .ui.xml is
<!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:e='urn:import:com.google.gwt.editor.ui.client'
ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
ui:generateKeys="com.google.gwt.i18n.server.keygen.MD5KeyGenerator"
ui:generateLocales="en,ru">
<ui:style src="../common.css"/>
<g:CaptionPanel captionText="Test" ui:field="captionPanel">
<g:HTMLPanel>
<table class="{style.forform}">
<tr>
<th class="{style.forform}">
<div>
<ui:msg meaning="person's name">Name:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="name" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
<tr>
<th class="{style.forform}">
<div>
<ui:msg>Phone Number:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="phoneNumber" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox width="100%" stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
<tr>
<th class="{style.forform}">
<div>
<ui:msg>EMail:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="email" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox width="100%" stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
</table>
</g:HTMLPanel>
</g:CaptionPanel>
</ui:UiBinder>
It works nicely.
Here is editor for Organization:
public class OrganizationEditor extends Composite implements Editor<OrganizationProxy>
{
interface OrganizationEditorUiBinder extends UiBinder<Widget, OrganizationEditor>
{
}
private static OrganizationEditorUiBinder uiBinder = GWT.create(OrganizationEditorUiBinder.class);
#UiField
CaptionPanel captionPanel;
#UiField
ValueBoxEditorDecorator<String> name;
#UiField
ValueBoxEditorDecorator<String> address;
#UiField
PersonEditor personEditor;
public void setCaptionText(String captionText)
{
captionPanel.setCaptionText(captionText);
}
public OrganizationEditor()
{
initWidget(uiBinder.createAndBindUi(this));
}
}
and its corresponding .ui.xml is
<!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:e='urn:import:com.google.gwt.editor.ui.client'
xmlns:c='urn:import:com.zasutki.courierApp.client.customer'
xmlns:myui='urn:import:com.zasutki.courierApp.client.ui'
ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
ui:generateKeys="com.google.gwt.i18n.server.keygen.MD5KeyGenerator"
ui:generateLocales="en,ru">
<ui:style src="../common.css"/>
<g:CaptionPanel ui:field="captionPanel">
<myui:VerticalFlowPanel>
<g:HTMLPanel>
<table class="{style.forform}">
<tr>
<th class="{style.forform}">
<div>
<ui:msg meaning="Name of organization">Name:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="name" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
<tr>
<th class="{style.forform}">
<div>
<ui:msg>Address:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="address" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
</table>
<c:PersonEditor ui:field="personEditor" captionText="Contact person">
<ui:attribute name="captionText"/>
</c:PersonEditor>
</g:HTMLPanel>
</myui:VerticalFlowPanel>
</g:CaptionPanel>
</ui:UiBinder>
interface for proxy of Organization is
#ProxyFor(value = Organization.class, locator = ObjectifyLocator.class)
public interface OrganizationProxy extends EntityProxy
{
public String getName();
public void setName(String name);
public String getAddress();
public void setAddress(String address);
public PersonProxy getContactPerson();
public void setContactPerson(PersonProxy contactPerson);
}
and finally here is class that uses all described above
public class NewOrderView extends Composite
{
interface Binder extends UiBinder<Widget, NewOrderView>
{
}
private static Binder uiBinder = GWT.create(Binder.class);
// Empty interface declaration, similar to UiBinder
interface OrganizationDriver extends SimpleBeanEditorDriver<OrganizationProxy, OrganizationEditor>
{
}
OrganizationDriver driver = GWT.create(OrganizationDriver.class);
#UiField
Button save;
#UiField
OrganizationEditor orgEditor;
OrganizationProxy organizationProxy;
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
organizationProxy = createFactory().contextOrder().create(OrganizationProxy.class);
// Initialize the driver with the top-level editor
driver.initialize(orgEditor);
// Copy the data in the object into the UI
driver.edit(organizationProxy);
}
#UiHandler("save")
void buttonClick(ClickEvent e)
{
e.stopPropagation();
OrganizationProxy edited = driver.flush();
PersonProxy person = edited.getContactPerson();
// person is always null !!!
if (driver.hasErrors())
{
}
}
}
The question is why nested editor (PersonEditor) doesn't get flushed automatically ? Is it supposed to happen ? What is the proper solution ?
<e:ValueBoxEditorDecorator ui:field="contactPerson">
<e:valuebox>
<c:PersonEditor captionText="Contact person">
<ui:attribute name="captionText"/>
</c:PersonEditor>
</e:valuebox>
</e:ValueBoxEditorDecorator>
This code throws the exception. After <e:valuebox> a sub-type of a ValueBox is expected (e.g. TextBox, DoubleBox,...). Your PersonEditor is not a ValueBox (and it makes no sense to make it one). So just add your PersonEditor in the OrganizationEditor's ui.xml like a normal Widget.
For example:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
....>
<ui:style src="../common.css"/>
<g:CaptionPanel ui:field="captionPanel">
<myui:VerticalFlowPanel>
<g:HTMLPanel>
<table class="{style.forform}">
// your other input fields
</table>
</g:HTMLPanel>
// Add the PersonEditor to the FlowPanel
<c:PersonEditor captionText="Contact person">
<ui:attribute name="captionText"/>
</c:PersonEditor>
</myui:VerticalFlowPanel>
</g:CaptionPanel>
</ui:UiBinder>
In the Java class, change
#UiField
ValueBoxEditorDecorator<PersonEditor> contactPerson;
to
#UiField
PersonEditor contactPerson;
Ah... I have to create proxy for contactPerson manually!
public class NewOrderView extends Composite
{
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
AdminRequestFactory.OrderRequestContext orderRequestContext = createFactory().contextOrder();
organizationProxy = orderRequestContext.create(OrganizationProxy.class);
organizationProxy.setContactPerson(orderRequestContext.create(PersonProxy.class));
// Initialize the driver with the top-level editor
driver.initialize(orgEditor);
// Copy the data in the object into the UI
driver.edit(organizationProxy);
}
}

GWT Popup and UIBinder: Panel or DialogBox?

I have a requirement where, when a button is clicked, the user should be prompted with a popup/dialog box to enter some additional details such as last name, DOB, etc.I tried to play with window.confirm() but I think this does not serve my purpose. Can some help me how this can be achieved in GWT through UIBinder?
I tried some thing like this in my UI binder.xml
<g:HTMLPanel visible="false" >
<g:DialogBox ui:field="dialogPanel"
animationEnabled="true" modal="false" glassEnabled="false">
<g:caption>More Details</g:caption>
<table>
<tr>
<td colspan="2" align="center">
<g:Datepicker ui:field="DOB">DOB:</g:Datepicker>
</td>
</tr>
<tr>
<td>UserName:</td>
<td>
<g:TextBox ui:field="usernameTextBox" />
</td>
</tr>
<tr>
<td></td>
<td align="right">
<g:Button ui:field="loginButton">OK</g:Button>
</td>
</tr>
</table>
</g:DialogBox>
</g:HTMLPanel>
I am not sure which one to go with: popup or dialog box!
Thanks.
Here is the skeleton for a GWT Dialog box using uibinder:
MyDialogBox.java
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.Widget;
public class MyDialogBox extends DialogBox {
private static final Binder binder = GWT.create(Binder.class);
interface Binder extends UiBinder<Widget, MyDialogBox> {
}
public MyDialogBox() {
setWidget(binder.createAndBindUi(this));
setAutoHideEnabled(true);
setText("My Title");
setGlassEnabled(true);
center();
}
}
MyDialog.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'>
<ui:style>
.panel {
background-color: ivory;
}
</ui:style>
<g:FlowPanel styleName="{style.panel}">
<g:Label>Dialog Content</g:Label>
</g:FlowPanel>
</ui:UiBinder>
show it using:
MyDialogBox m = new MyDialogBox();
m.show();
DialogBox is a child of PopupPanel and has all its features. Additionally it has (from the docs):
..caption area at the top and can be dragged by the user.
About usage in UiBinder (again from the docs):
DialogBox elements in UiBinder templates can have one widget child and one
<g:caption> child.
So it seems that you need to replace your <table> with a GWT widget, most probably <g:HTMLPanel> and put whole <table> inside it.
Also, PopupPanel and DialogBox are standalone widgets and normally do not get added to other Widgets, but are shown via .show() and hide() methods. So, in your UiBinder, you can put
<g:DialogBox> at the root level.

uiBinder on Button Clickevent

I'm trying to use uiBinder. I followed the tutorial provided by
google, but I don't know why clickevent doesn't work? I want to count number of clicks and show it in the span, it doesn't work, I also put window.alert but it seems that the event handler is not called at all! Can anyone help me? It's couple of hours I'm working on it but can't find the problem!
Thank you so much
P.S.
Below is my code
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:style>
</ui:style>
<g:HTMLPanel>
<table>
<tr>
<td><img ui:field='imgPrd'/></td>
<td>
<span ui:field='lblNum'></span>
<g:Button ui:field='btnAdd'></g:Button>
</td>
</tr>
</table>
</g:HTMLPanel>
public class uiProductList extends Composite {
#UiField Button btnAdd;
#UiField ImageElement imgPrd;
#UiField SpanElement lblNum;
int count;
private static uiProductListUiBinder uiBinder =
GWT.create(uiProductListUiBinder.class);
interface uiProductListUiBinder extends UiBinder<Widget,
uiProductList> {
}
public uiProductList() {
initWidget(uiBinder.createAndBindUi(this));
}
#UiHandler("btnAdd")
void handleClick(ClickEvent e) {
Window.alert("test");
count++;
lblNum.setInnerText(Integer.toString(count));
}
}
You should correctly add your widget to the root panel. Use
RootPanel.get().add(uiProduct);
Otherwise the handlers are not initialized.
I had exactly the same problem and here is the conclusion:
RootPanel.getBodyElement().appendChild(uiProduct.getElement()); - NOT WORKING
RootPanel.get().add(uiProduct); - WORKING FINE