I am a newbie trying to use a MenuBar to swap the displayed panel in a DeckPanel.
I have 2 classes and 2 associated uibinder XML files:
ApplicationUi.java
ApplicationUi.ui.xml
ApplicationMenu.java
ApplicationMenu.ui.xml
In ApplicationUi.java and the UI XML, the root is bound to a DockLayoutPanel. The ApplicationMenu is meant to be in the North section of the DockLayoutPanel. The MenuBar options will affect the DeckPanel in the Center section.
In ApplicationMenu, how can I get a reference to the DeckPanel so I can call showWidget() to swap the displayed panel?
Also, since I'm a newb, any suggestions or reviews of this code are welcome. I've done the best I can on Google, but alot of what I'm looking for doesn't seem to be out there.
(This is a followup to Replace GWT DockLayoutPanel Contents).
Source:
ApplicationUi.java
import org.jason.datacenter.client.forms.NewRequirementForm;
public class ApplicationUi extends Composite {
private static final Binder binder = GWT.create(Binder.class);
interface Binder extends UiBinder<Widget, ApplicationUi> {
}
#UiField DockLayoutPanel dlp;
#UiField VerticalSplitPanel headerPanel;
#UiField DeckPanel deckPanel;
public ApplicationUi() {
initWidget(binder.createAndBindUi(this));
// add the NewRequirementForm to the deckpanel as index #0
deckPanel.add(new NewRequirementForm());
}
public void switchDeck(int newIndex) {
deckPanel.showWidget(newIndex);
}
}
ApplicationUi.ui.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!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:DockLayoutPanel ui:field="dlp">
<g:north size="800">
<g:VerticalSplitPanel ui:field="headerPanel">
</g:VerticalSplitPanel>
</g:north>
<g:center>
<g:DeckPanel ui:field="deckPanel" />
</g:center>
</g:DockLayoutPanel>
</ui:UiBinder>
ApplicationMenu.java:
public class ApplicationMenu extends Composite {
private static final Binder binder = GWT.create(Binder.class);
interface Binder extends UiBinder<Widget, ApplicationMenu> {
}
#UiField MenuBar applicationMenu;
#UiField MenuItem mitmNewPower;
public ApplicationMenu() {
initWidget(binder.createAndBindUi(this));
mitmNewPower.setCommand(new Command() {
#Override
public void execute() {
RootLayoutPanel rlp = RootLayoutPanel.get();
DockLayoutPanel dlp = (DockLayoutPanel) rlp.getWidget(0);
}
});
}
}
ApplicationMenu.ui.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!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:MenuBar ui:field="applicationMenu">
<g:MenuItem>
Process
<g:MenuBar>
<g:MenuItem ui:field="mitmNewPower" />
</g:MenuBar>
</g:MenuItem>
</g:MenuBar>
</ui:UiBinder>
One way you could do this would be to use an EventBus. Create an event type and have your ApplicationMenu fire an event of that type when a menu item gets clicked. The ApplicationUi object can subscribe to that event and respond to it by updating the contents of the DeckPanel. This avoids the menu object needing to know about the DeckPanel at all.
Related
I have simple view. There are uiBinder and class themselves:
public class NewNotePopupPanel extends Composite implements NewNoteView {
interface NewNotePopupPanelUiBinder extends UiBinder<PopupPanel, NewNotePopupPanel> {
}
private static NewNotePopupPanelUiBinder ourUiBinder = GWT.create(NewNotePopupPanelUiBinder.class);
#UiField
PopupPanel popupPanel;
#UiField
VerticalPanel newNoteMainPanel;
#UiField
HorizontalPanel newNoteHeader;
#UiField
Label storedNoteTitle;
#UiField
DateLabel noteCreatedDate;
#UiField
VerticalPanel contentPanel;
#UiField
TextBox currentNoteTitle;
#UiField
RichTextArea contentTextArea;
#UiField
HorizontalPanel newNoteFooter;
#UiField
CheckBox favorite;
#UiField
Button save;
#UiField
Button close;
private Presenter presenter;
static {
Resources.INSTANCE.style().ensureInjected();
}
public NewNotePopupPanel() {
initWidget(ourUiBinder.createAndBindUi(this));
}
#UiHandler("favorite")
void onFavoriteCheckBoxClicked(ClickEvent event) {
if (presenter != null) {
presenter.onFavoriteCheckBoxClicked();
}
}
#UiHandler("save")
void onApplyButtonClicked(ClickEvent event) {
if (presenter != null) {
presenter.onApplyButtonClicked();
}
}
#UiHandler("close")
void onCancelButtonClicked(ClickEvent event) {
popupPanel.hide();
}
}
UiBinder:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:with field="res" type="ru.beleychev.notes.client.ui.Resources"/>
<g:PopupPanel ui:field="popupPanel" width="600px" modal="true" title="Edit Note" addStyleNames="{res.style.mainPanel}">
<g:VerticalPanel ui:field="newNoteMainPanel">
<g:HorizontalPanel ui:field="newNoteHeader">
<g:Label ui:field="storedNoteTitle" addStyleNames="{res.style.label}"/>
<g:DateLabel ui:field="noteCreatedDate" customFormat="EEE, MMM d, yyyy"
addStyleNames="{res.style.label}"/>
</g:HorizontalPanel>
<g:VerticalPanel ui:field="contentPanel">
<g:TextBox ui:field="currentNoteTitle" addStyleNames="{res.style.searchBox}"/>
<g:RichTextArea ui:field="contentTextArea" focus="true"/>
</g:VerticalPanel>
<g:HorizontalPanel ui:field="newNoteFooter">
<g:CheckBox ui:field="favorite"/>
<g:Button ui:field="save" text="Save" addStyleNames="{res.style.button}"/>
<g:Button ui:field="close" text="Close" addStyleNames="{res.style.button}"/>
</g:HorizontalPanel>
</g:VerticalPanel>
</g:PopupPanel>
This popup window opens from another view. And there is all ok.
I have no problems with interface. But, unfortunately, "Close" button doesn't close popup. It's simple (easy-peasy). What is the problem? ) Looking forward to your suggestions, guys. Thank you in advance.
from why can't i hide DialogBox in UiBinder in GWT?
DialogBox (and PopupPanels in general) does not work like any other widget when speaking about adding them to the DOM. You should never attach them directly to it (i.e., panel.add(yourDialogBox) or inside a UiBinder XML file) as you did. Instead you should create them, and simply call hide()/show(), and the like methods, to get it displayed/hidden (i.e., attached/detached at the end of/from the DOM)
I am trying to render a Celltable with UiBinder but I only get a blank screen.
Here is my code:
VDataGrid.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:c="urn:import:com.google.gwt.user.cellview.client">
<ui:style>
.cellTable {
border-bottom: 1px solid #ccc;
text-align: left;
margin-bottom: 4px;
}
</ui:style>
<g:HTMLPanel>
<table cellspacing='0' cellpadding='0' style='width:100%;'>
<tr>
<td valign='top'>
<c:CellTable addStyleNames='{style.cellTable}'
pageSize='15' ui:field='cellTable' />
</td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>
VDataGrid.java
public class VDataGrid extends ResizeComposite {
interface Binder extends UiBinder<Widget, VDataGrid> {
}
interface SelectionStyle extends CssResource {
String selectedRow();
}
private static final Binder binder = GWT.create(Binder.class);
#UiField(provided = true)
CellTable<Contact> cellTable;
public VDataGrid() {
initWidget(binder.createAndBindUi(this));
TextColumn<Contact> nameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact object) {
return object.name;
}
};
cellTable.addColumn(nameColumn, "Name");
DateCell dateCell = new DateCell();
Column<Contact, Date> dateColumn = new Column<Contact, Date>(dateCell) {
#Override
public Date getValue(Contact object) {
return object.birthday;
}
};
cellTable.addColumn(dateColumn, "Date");
TextColumn<Contact> addressColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact object) {
return object.address;
}
};
cellTable.addColumn(addressColumn, "Address");
final ListDataProvider<Contact> dataProvider = new ListDataProvider<Contact>();
// Connect the table to the data provider.
dataProvider.addDataDisplay(cellTable);
// Add the data to the data provider, which automatically pushes it to
// the
// widget.
List<Contact> list = dataProvider.getList();
for (Contact contact : CONTACTS) {
list.add(contact);
}
}
And here is the code for the class using the above
DataGrid.ui.xml
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:grid='urn:import:com.grid.client'>
<g:DockLayoutPanel unit='EM'>
<g:center>
<g:ScrollPanel>
<grid:VDataGrid ui:field='grid' />
</g:ScrollPanel>
</g:center>
</g:DockLayoutPanel>
</ui:UiBinder>
Entrypoint class : DataGrid.java
public class DataGrid implements EntryPoint {
interface Binder extends UiBinder<DockLayoutPanel, DataGrid> { }
private static final Binder binder = GWT.create(Binder.class);
#UiField VDataGrid grid;
#Override
public void onModuleLoad() {
// Create the UI defined in Mail.ui.xml.
DockLayoutPanel outer = binder.createAndBindUi(this);
Window.enableScrolling(false);
Window.setMargin("0px");
RootLayoutPanel root = RootLayoutPanel.get();
root.add(outer);
}
}
I only see a blank screen. Appreciate some insights into this.
Most of the time, if you don't see your CellTable, it's because it has a height of zero.
You put your CellTable in the HTMLPanel. Neither HTMLPanel, nor CellTable implement ProvidesResize and/or RequireResize interfaces, which means that their height has to be set explicitly - they won't get it from their parent widgets.
Also, there is no need to put CellTable inside the table tag - it serves no purpose. In fact, you don't need to put it inside the HTMLPanel either.
in Test.ui.xml
<g:DialogBox ui:field="wishlistDialogBox" autoHide="true">
<g:caption>Test</g:caption>
<g:HTMLPanel> some widgets..</g:HTMLPanel>
</g:DialogBox>
After running, the application still show the DialogBox, so I tried to set hide for "wishlistDialogBox" in TestView.java but it didn't work.
#UiField DialogBox wishlistDialogBox;
#Inject
public TestView(final Binder binder) {
widget = binder.createAndBindUi(this);
wishlistDialogBox.hide();
}
Then i set hide for it in TestPresenter.java but it still didn't work
#Override
protected void onBind() {
super.onBind();
getView().getWishlistDialogBox().hide();
}
What's wrong, Goodle didn't explain it at all.
In addition, how to reuse the DialogBox?
DialogBox (and PopupPanels in general) does not work like any other widget when speaking about adding them to the DOM. You should never attach them directly to it (i.e., panel.add(yourDialogBox) or inside a UiBinder XML file) as you did. Instead you should create them, and simply call hide()/show(), and the like methods, to get it displayed/hidden (i.e., attached/detached at the end of/from the DOM).
Something that works for me is creating a Dialogbox separately from any other widgets. So it has its own Java file and its own ui.xml file :
UiBinder xml file:
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:DialogBox ui:field="dialog">
<g:caption>My Dialog</g:caption>
<g:HTMLPanel>
<g:Button ui:field="closeButton" text="close" />
</g:HTMLPanel>
</g:DialogBox>
</ui:UiBinder>
Java file:
public class MyDialog { // here you do not inherit anything
private static MyDialogUiBinder uiBinder = GWT.create(MyDialogUiBinder.class);
interface MyDialogUiBinder extends UiBinder<Widget, MyDialog> {
}
#UiField
DialogBox dialog;
#UiField
Button closeButton;
public MyDialog() {
// make cast to DialogBox
dialog = (DialogBox) (uiBinder.createAndBindUi(this));
}
public void hide() {
dialog.hide();
}
public void show() {
dialog.center();
}
#UiHandler("closeButton")
public void onClick(ClickEvent event) {
hide();
}
}
Finally i figured out a way, that is to put the DialogBox into a invisible HTMLPanel
<g:HTMLPanel visible="false">
<g:DialogBox ui:field="wishlistDialogBox" autoHide="true">
<g:caption>Test</g:caption>
<g:HTMLPanel> some widgets..</g:HTMLPanel>
</g:DialogBox>
</g:HTMLPanel>
Then just call show & hide DialogBox as usual & it will show the DialogBox even the DialogBox was wrapped inside an invisible HTMLPanel.
getView().getWishlistDialogBox().show();
Using GWT 2.5.0,
I would like to use Client side validation and Editors. I encounter the following error when trying to pass the ConstraintViolation java.util.Set to the EditorDriver as follows.
Validator a = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Person>> b = a.validate(person);
editorDriver.setConstraintViolations(b);
The method setConstraintViolations(Iterable<ConstraintViolation<?>>) in the type EditorDriver<Person> is not applicable for the arguments (Set<ConstraintViolation<Person>>)
The only somewhat relevant post I could find was Issue 6270!
Below is an Example which brings up a PopUpDialog with a Person Editor that allows you to specify a name and validate it against your annotations. Commenting out the personDriver.setConstraintViolations(violations); line in the PersonEditorDialog will allow you to run the example.
I don't have enough reputation points to post the image of the example.
Classes
Person
public class Person {
#NotNull(message = "You must have a name")
#Size(min = 3, message = "Your name must contain more than 3 characters")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
PersonEditorDialog
public class PersonEditorDialog extends DialogBox implements Editor<Person> {
private static PersonEditorDialogUiBinder uiBinder = GWT
.create(PersonEditorDialogUiBinder.class);
interface PersonEditorDialogUiBinder extends
UiBinder<Widget, PersonEditorDialog> {
}
private Validator validator;
public PersonEditorDialog() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
setWidget(uiBinder.createAndBindUi(this));
}
interface Driver extends SimpleBeanEditorDriver<Person, PersonEditorDialog> {
};
#UiField
ValueBoxEditorDecorator<String> nameEditor;
#UiField
Button validateBtn;
private Driver personDriver;
#UiHandler("validateBtn")
public void handleValidate(ClickEvent e) {
Person created = personDriver.flush();
Set<ConstraintViolation<Person>> violations = validator
.validate(created);
if (!violations.isEmpty() || personDriver.hasErrors()) {
StringBuilder violationMsg = new StringBuilder();
for (Iterator<ConstraintViolation<Person>> iterator = violations.iterator(); iterator.hasNext();) {
ConstraintViolation<Person> constraintViolation = (ConstraintViolation<Person>) iterator
.next();
violationMsg.append(constraintViolation.getMessage() + ",");
}
Window.alert("Detected violations:" + violationMsg);
personDriver.setConstraintViolations(violations);
}
}
#Override
public void center() {
personDriver = GWT.create(Driver.class);
personDriver.initialize(this);
personDriver.edit(new Person());
super.center();
}
}
SampleValidationFactory
public final class SampleValidationFactory extends AbstractGwtValidatorFactory {
/**
* Validator marker for the Validation Sample project. Only the classes and
* groups listed in the {#link GwtValidation} annotation can be validated.
*/
#GwtValidation(Person.class)
public interface GwtValidator extends Validator {
}
#Override
public AbstractGwtValidator createValidator() {
return GWT.create(GwtValidator.class);
}
}
EditorValidationTest
public class EditorValidationTest implements EntryPoint {
/**
* This is the entry point method.
*/
public void onModuleLoad() {
PersonEditorDialog personEditorDialog = new PersonEditorDialog();
personEditorDialog.center();
}
}
UiBinder
PersonEditorDialog.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:e="urn:import:com.google.gwt.editor.ui.client">
<ui:style>
.important {
font-weight: bold;
}
</ui:style>
<g:HTMLPanel>
<g:Label>Enter your Name:</g:Label>
<e:ValueBoxEditorDecorator ui:field="nameEditor">
<e:valuebox>
<g:TextBox />
</e:valuebox>
</e:ValueBoxEditorDecorator>
<g:Button ui:field="validateBtn">Validate</g:Button>
</g:HTMLPanel>
</ui:UiBinder>
GWT Module
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.5.0//EN"
"http://google-web-toolkit.googlecode.com/svn/tags/2.5.0/distro-source/core/src/gwt-module.dtd">
<module rename-to='editorvalidationtest'>
<inherits name='com.google.gwt.user.User' />
<inherits name='com.google.gwt.user.theme.clean.Clean' />
<inherits name="com.google.gwt.editor.Editor"/>
<!-- Validation module inherits -->
<inherits name="org.hibernate.validator.HibernateValidator" />
<replace-with
class="com.test.client.SampleValidationFactory">
<when-type-is class="javax.validation.ValidatorFactory" />
</replace-with>
<!-- Specify the app entry point class. -->
<entry-point class='com.test.client.EditorValidationTest' />
<!-- Specify the paths for translatable code -->
<source path='client' />
<source path='shared' />
</module>
Libs required on Classpath
hibernate-validator-4.1.0.Final.jar
hibernate-validator-4.1.0.Final-sources.jar
validation-api-1.0.0.GA.jar (in GWT SDK)
validation-api-1.0.0.GA-sources.jar (in GWT SDK)
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.1.jar
log4j-1.2.16.jar
As discussed in the comments, the following cast was determined to be a valid workaround.
Set<?> test = violations;
editorDriver.setConstraintViolations((Set<ConstraintViolation<?>>) test);
This is what I do over and over again :
List<ConstraintViolation<?>> adaptedViolations = new ArrayList<ConstraintViolation<?>>();
for (ConstraintViolation<Person> violation : violations) {
adaptedViolations.add(violation);
}
editorDriver.setConstraintViolations(adaptedViolations);
The driver has a wild card generic type defined and you can not pass in the typed constraint violations.
For testing purposes, I want to use a DialogBox for logging into my application.
Here's the uibinder file:
<!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>
</ui:style>
<g:HTMLPanel>
<g:Label>Username</g:Label>
<g:TextBox ui:field="username"></g:TextBox>
<g:Label>Password</g:Label>
<g:PasswordTextBox ui:field="password"></g:PasswordTextBox>
<g:Button ui:field="login">Login</g:Button>
</g:HTMLPanel>
</ui:UiBinder>
And here's my implementation of it:
public class Login extends DialogBox {
private static LoginUiBinder uiBinder = GWT.create(LoginUiBinder.class);
interface LoginUiBinder extends UiBinder<Widget, Login> {}
#UiField
TextBox username;
#UiField
PasswordTextBox password;
#UiField
Button login;
public Login() {
setHTML("Login");
setWidget(uiBinder.createAndBindUi(this));
}
}
Now my question is: Is this the correct way to do it? The documentation doesn't seem to say anything on how to do this sort of thing...
That's what I do, and it's been working great in production for months. It's super easy to understand and reuse.
I made an abstract dialog with the same pattern that has an abstract method onConfirm and a built-in confirm button. I also include in the UiBinder a panel to accept a widget, and override the setWidget function to put the widget into that interior panel. Then, whenever I need a new dialog for something, I can just write:
final CustomWidget whicheverWidgetINeedRightNow = xyz;
CustomDialog dialog = new CustomDialog()
{
#Override
protected void onConfirm()
{
process(whicheverWidgetINeedRightNow.getData());
}
};
dialog.setWidget(whicheverWidgetINeedRightNow);
The ok button in the template UiBinder is hard-wired to call onConfirm when it's pressed. Nifty! For more complex cases, I'd subclass CustomDialog in its own named class.
It's worked well for me in maybe 5 or 6 different situations in my app, and I don't have to re-style or re-code anything.