GWT - How to define a Widget outside layout hierarchy in uibinder xml file - gwt

this is my first post. I hope someone could help me.
I'm looking for a way to define a widget in UiBinder XML layout file separately, without being part of the layout hierachy.
Here's a small example:
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:Label ui:field="testlabel" text="Hallo" />
<g:HTMLPanel>
...
</g:HTMLPanel>
The compile fails since the ui:UiBinder element expects only one child element.
In Java Code i will access and bind the Label widget as usual:
#UiField Label testlabel;
For example, this could be useful when you define a Grid or FlexTable - i want to define the Labels for the table header within the XML layout file, not programmatically within the code.
Many thanks in advance

Sorry, no can do, UiBinder just doesn't support this sort of syntax. Think about it - where would this Widget go? Should it be added to the DOM tree? It also makes it hard for the compiler to tell if this code should be pruned as unused.
You'll have to wait for the GWT team to create custom tags for Grid (like they did with, for example, DockLayoutPanel). I wouldn't expect such functionality for the FlexTable though, since it's meant by design for dynamic content, meaning adding it programmatically anyway.

Understanding the UiBinder Dev Guide is the key to figuring out how to structure your UiBinder templates cleanly.
So, if all you want to do is create a widget and not have it initially in the DOM, then don't mention it at all in the UiBinder template. Instead, in the Java file that goes with your UiBinder template, create it and add it to a panel that's defined in your template.
For instance, put this in your template:
<g:HTMLPanel ui:field='container'>
...
And put this in your Java file:
#UiField HTMLPanel container;
public MyWidget() {
initWidget(uiBinder.createAndBindUi(this));
Label testLabel = new Label("hallo");
// Do whatever you want to testLabel
container.add(testLabel);
}

I am not certain if you and have the same unusual motivation to place more than two root widgets under the uibinder tag. Anyway, this is how I do it.
Since uibinder tag allows only one root widget, I place an HTML ui tag as the root widget, and then pile up my multiple pseudo-root widgets within that tag.
In the following example, notice that the actual root widget does not have ui:field name, because we don't intend to use it. As far as we are concerned, the effective root widgets are "tabLayout" and "content".
File Hello.ui.xml:
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTML>
<g:TabLayoutPanel ui:field="tabLayout" ... >
blah ... blah
</g:TabLayoutPanel>
<g:VerticalPanel ui:field="content">
blah .. blah
</g:VerticalPanel>
</g:HTML>
</ui:UiBinder>
Do not extend composite as your java code behind the template.
Create a class imported by your module entrypoint or use the template directly on your module.
I have various reasons why I need more than one root widget. In this example, I attach the "effective root widget" conditionally:
public class Hello
implements EntryPoint{
private static HelloUiBinder uiBinder = GWT.create(HelloUiBinder.class);
interface HelloUiBinder
extends UiBinder<Widget, Hello>{}
#UiField TabLayoutPanel tabLayout;
#UiField VerticalPanel content;
#Override public void onModuleLoad() {
uiBinder.createAndBindUi(this);
if (condition1)
RootPanel.get().add(tabLayout);
else
RootPanel.get().add(content);
blah ... blah
}
}
So, the trick is not to use the actual root widget at all.

Related

GWT DataGrid Doesn't Display although in LayoutPanels and added to RootLayoutPanel

I am creating a GWT app with a DataGrid. The DataGrid hierarchy is:
RootLayoutPanel->DockLayoutPanel->DockLayoutPanel->LayoutPanel->DockLayoutPanel->DockLayoutPanel->DataGrid
Apart from the adding to RootLayoutPanel, the structure is all defined in uiBinder XML
The DataGrid is included in the XML using the following code
<g:ScrollPanel>
<c:DataGrid ui:field='ownershipGrid' visible="true"/>
</g:ScrollPanel>
In my view I have
#UiField
DataGrid<OwnershipInformation> ownershipGrid;
...
ownershipGrid = new DataGrid<OwnershipInformation>(OwnershipInformation.KEY_PROVIDER);
ownershipGrid.setMinimumTableWidth(140, Style.Unit.EM);
ownershipGrid.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy .KeyboardSelectionPolicy.ENABLED);
... add comlumns to ownershipGrid ...
ownershipInformationList.addDataDisplay(ownershipGrid);
However, I still don't get my table to show. I have called ownershipGrid.getRowCount() and it shows as having 6 rows, so I know that data is there. The panels in which the DataGrid is contained are all visible.
Regards, Richard
The problem is that after adding the DataGrid in the uiBinder, I then call new and point the private variable to a different DataGrid that isn't being displayed.
You've forgot to declare the grid provided:
#UiField DataGrid<OwnershipInformation> ownershipGrid;
should be:
#UiField(provided=true) DataGrid<OwnershipInformation> ownershipGrid;

Why there are so many way to set CSS for Widgets in GWT or GWTP, I am confused? Can someone clarify this?

Ok, to set a Css for a widget in Gwt or Gwtp, we can do following:
-1. directly from gwt code. Ex: label1.getElement().getStyle().setBackground("blue");
-2. include "ui:style" in UiBinder xml file, but this css only visible to that UiBinder
-3. include "ui:width" in UiBinder xml file, it will visible to all UiBinder
- and there r many way to set the Css directly to the widget in UiBinder.
The one that made me confused is that if i used , ex
<ui:with field='res' type="com.myproj.client.MyResource" />
& if myResource.css has .gwt-TabLayoutPanel then i don't need to use "addStyleNames", ex <g:TabLayputPanel />, it can recognized CSS perfectly.
However, if i add .gwt-ScrollPanel into myResource.css & use <g: ScrollPanel /> then nothing happened.
So I have to create public interface MyCssResource extends CssResource, then add String gwt-ScrollPanel(); to MyCssResource. But Java eclipse do not allow hyphen - in the method name, so I have to change to String gwtScrollPanel();.
Finally, i have to add addStyleNames into <g: ScrollPanel />, ex <g: ScrollPanel addStyleNames="{res.css.gwtScrollPanel}" /> then it will work.
That also means if i want to use .gwt-TabLayoutPanel in MyCssResource, then i need to remove the hyphen - & this will cause inconsistency in my code.
So, can someone explain to me what is going on here? I am confused?
The only think you should be aware about, is that GWT obfuscates class names when you use then in ui-binders. For instance:
<ui:style>
.gwtTabLayoutPanel {}
</ui:style>
<g:TabLayoutPanel addStyleNames="{style.gwtTabLayoutPanel}" />
.gwtTabLayoutPanel will be renamed to something like .AB in the final injected style-sheet.
But most GWT widgets, are styled with un-obfuscated class names, so for using them in ui-binders files, you have to define as external in order to prevent GWT compier to obfuscate the class name:
<ui:style>
#external .gwt-TabLayoutPanel;
.gwt-TabLayoutPanel {}
</ui:style>
<g:TabLayoutPanel />
That's because when you create a TabLayoutPanel, it has a default class called .gwt-TabLayoutPanel. So you don't need to add that class manually in to your TabLayoutPanel.Just create a TabLayoutPanel and you will see the class ".gwt-TabLayoutPanel" is already there.
But ScrollPanel doesn't comes with a default class called .gwt-ScrollPanel. It is just a div. Try creating a ScrollPanel and see. It doesn't have any classes added initially.see the screenshot
If you want to add a class called .gwt-ScrollPanel you will have to do it manually.

Custom tags in UiBinder files

When using a <g:LayoutPanel> in UiBinder.ui.xml files, you can specify <g:layer> tags. Some other Google-built widgets have special tags like that as well - <g:tab> even has a sub-tag, <g:header>.
How can I specify these for my own widgets?
The new answer to this question, after some GWT improvements, is at https://stackoverflow.com/a/11785903/439317 . Copied below to avoid moderator deletion (maybe?).
You can use #UiChild to declare special functions in your widgets accessible in UiBinders.
for example,
class MyPanel extends AbsolutePanel {
#UiChild
public void addAt(Widget w, String parameter1, String parameter2) {
....
Then, in your uiBinder, you can say
<custom:MyPanel>
<custom:at parameter1="HI" parameter2="Anything you like!">
<g:AnySingleWidget />
</custom:at>
</custom:MyPanel>
See #UiChild at http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/uibinder/client/UiChild.html
What you're looking for is a custom element parser for UiBinder. See this issue. Unfortunately it's not supported yet.
You might be interested in this post for some guidance on how to extend the current parser on your own.

How to replace element with my GWT widget?

Is it possible to replace known html element with my widget component? (Emphasis on the word 'replace', I don't want to put the widget in that element. :)
<body>
<img />
<div />
<a id="tmpEl" />
...
<img />
</body>
would become
<body>
<img />
<div />
<div class="gwt-panel">...</div>
...
<img />
</body>
I tried something like this...
tmpEl.getParentElement().replaceChild(myPanel.getElement(), tmpEl);
...but the resulting DOM elements were 'deaf', i.e. they did not receive click events. (To make this work I would probably have to call RootPanel.get().adopt(widget), but that method is not accessible.)
For a second I thought HTMLPanel.addAndReplaceElement could be the answer, but that only works when your 'placeholder' element is (direct) child of HTMLPanel widget. Which is obviously not my case. :(
Note please that I only know id of that element, I'm not creating it. Simply put: I need exactly what the question says.
As for 'DOM manipulation at higher level': I will happily manipulate the DOM at highest possible level if it lets me place widget instead of that placeholder element.
It seems that calling widget.onAttach() after inserting widget into DOM does the trick.
class MyWidget extends Composite
{
...
public void attach()
{
/* Widget.onAttach() is protected
*/
onAttach();
/* mandatory for all widgets without parent widget
*/
RootPanel.detachOnWindowClose(this);
}
}
tmpEl.getParentElement().replaceChild(myWidget.getElement(), tmpEl);
myWidget.attach();
Credit goes to André at Google Web Toolkit group.
I still wonder though why there is no RootPanel.addAndReplaceElement(Widget, Element), similar to HTMLPanel.addAndReplaceElement(Widget, Element).
The solution is probably not so much different from what Igor suggested. I'd write something like this:
RootPanel rootPanel = RootPanel.get();
Element anchorElement = DOM.getElementById("tmpEl");
Anchor anchor = Anchor.wrap(anchorElement);
rootPanel.remove(anchor);
rootPanel.insert(new HTML("<div class='gwt-panel'>...</div>", 0);
I agree with markovuksanovic - you should consider DOM manipulation on a "higher level". For example, the functionality you need is provided via the InsertPanel interface, which FlowPanel implements:
FlowPanel mainPanel = new FlowPanel();
Hyperlink link = new Hyperlink("Something cool");
mainPanel.add(link);
mainPanel.add(new Label("Bla bla"));
// Now we want to replace the first element of the FlowPanel with a TextBox
// We can do that by its index...
mainPanel.remove(0);
// ...or Widget instance
mainPanel.remove(link);
// Now we add a TextBox at the beginning
mainPanel.add(new TextBox(), 0);
As you can see, this code is much more readable (at least to me), you don't manipulate the DOM directly (referencing via ids and such). Of course, there are places where direct DOM manipulation is beneficial (optimizing, most notably), but for the most part, I'd avoid juggling Elements, ids and such :) (at least in GWT)
I'm doing something pretty similar. I'm reading the contents of a <div>; using the elements found in the <div> to build an animated menu which then replaces the original content. The animated menu is a widget.
It seems to work...
// DisclosurePanel is a widget
DisclosurePanel accordion_panel = processAccordion(accordionElement);
// accordionElement is found in the DOM (it's a com.google.gwt.user.client.Element)
// clear what was there
accordionElement.setInnerText("");
// add the widget in
RootPanel.get(accordionElement.getId()).add(accordion_panel);
I would suggest you try something like this...
<body>
<a id="tmpEl" />
</body>
then in somewhere create a widget (e.g. textBox widget)
TextBox tb = new TextBox();
After you have created the widget you can get it's DOM object by using tb.getElement() (I'm not sure that's the correct method name, but it's definitely something like getElement)
After that you could use
Element parent = Document.getElementById('tmpEl').getParent();
parent.removeChild(Document.getElementById('tmpEl'));
parent.appendChild(tb.getElement());
One more thing that add() does is call Widget.onAttach() on the widget that is being added to the panel. onAttach does some work to register the widget to receive events.
You could try calling tb.onAttach(); It might also be necessary to call RootPanel.detachOnWindowClose(tb); (as mentioned in a previous post)

GWT CssResource Customization

I'm writing a GWT widget using UIBinder and MVP. The widget's default styles are defined in TheWidgetView.ui.xml:
<ui:style type="com.widgetlib.spinner.display.TheWidgetView.MyStyle">
.textbox {
border: 1px solid #red;
}
.important {
font-weight: bold;
}
</ui:style>
The widget's CssResource interface is defined in TheWidgetView.java:
public class TheWidgetView extends HorizontalPanel implements TheWidgetPresenter.Display {
// ... some code
#UiField MyStyle style;
public interface MyStyle extends CssResource {
String textbox();
String important();
}
// ... more code
}
I'd like the consumer of this widget to be able to customize part of the widget's styles and to have this in their MyExample.ui.xml:
<ui:style type="com.consumer.MyExample.MyStyle">
.textbox {
border: 2px solid #black;
}
</ui:style>
And this be their MyExample.java:
public class MyExample extends Composite {
// ... some code
#UiField MyStyle style;
interface MyStyle extends TheWidgetView.MyStyle{
String textbox();
}
// ... more code
}
Is there a way that my widget can have default styles, but that the consumer of the widget can override one of them? When an interface extends TheWidgetView.MyStyle, the of the widget consumer needs to define all the styles listed in that parent interface. I've seen some widget libraries have the widget's constructor take in a ClientBundle as parameter, which I suppose could apply to CssResource. Although, I'm not sure how I'd pass in this style object in a constructor invoked by UIBinder.
Thanks much in advance!
I have playing around with something similar to make my application skinable. I would start by looking at the source code for any of the cell widgets. They seem to take the resources as a constructor, however they also have constructors that use GWT.create(Resources.class) to create the resources if they are not provided. As far as your question about using this template for UIBinder, there are 3 options mentioned here. However you may run into chicken and the egg issues when trying to define the style inside the UIBinder.
The issue you are running into with your other code is that your 2 different implementations of the style because uibinder creates it's own resource bundle which doesn't reference the parent's css. There are 3 solutions:
1) Tear the css out of the ui binder file into it's own file and use ui:with combined with either a provided field or uifactory to inject the style using your own resource bindle where you can compound the sources (i.e. #Source({DEFAULT_CSS, "myCss.css"})).
2) Your other option is to look at the generated code and use the syntax they are using to refernce the css within the uibinder file, however there are 2 issues you will have to overcome: the chicken and the egg problem and the fact that google can change this without telling you and break your stuff. Here is an example of the generated client bundle from one of my files:
#Source("uibinder:com.foo.gwt.client.workspace.select.SelectWorkspaceViewImpl_SelectWorkspaceViewImplUiBinderImpl_GenCss_style.css")
SelectWorkspaceViewImpl_SelectWorkspaceViewImplUiBinderImpl_GenCss_style style();
3) The last solution is to use deferred binding to replace the default style.
<replace-with class="com.foo.gwt.laf.mpe.resource.CellTableResources">
<when-type-is class="com.google.gwt.user.cellview.client.CellTable.Resources" />
<when-property-is name="laf" value="MPE" />
</replace-with>