I am creating an Anchor as follows:
Anchor a = new Anchor( "Click Me" );
Then I add a click handler:
a.addClickHandler( new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
GWT.log("Anchor Clicked");
}
} );
I want to add the Anchor to a LI element. Since I don't have any Widgets for UL and LI, I use the following construct:
public class ElementPanel extends ComplexPanel {
public ElementPanel(String tagName) {
setElement(DOM.createElement(tagName));
}
#Override
public void add(Widget w) {
add(w, getElement());
}
}
I create my UL element:
ElementPanel ul = new ElementPanel(UListElement.TAG);
I create a LI element:
ElementPanel li = new ElementPanel(LIElement.TAG);
I add my Anchor to LI, then I add LI to UL (after which I add to the document):
li.add(a);
ul.add(li);
This works fine. If instead I change the previous lines as follows, I don't see a log message:
li.getElement().appendChild(a.getElement());
ul.add(li);
Similarly, if instead I try this, I also do not see a message:
li.add(a);
ul.getElement().appendChild(li.getElement());
Previously, I had the UL element in the UIBinder. But since I was not successful in adding a click handler to an Element, I have to resort to the above approach.
This is how events are handled in GWT: the widget needs to be attached, and that generally means adding it to a container chain up to a RootPanel. See https://code.google.com/p/google-web-toolkit/wiki/DomEventsAndMemoryLeaks#GWT's_Solution for the details.
The easiest way to have <ul><li> if your structure doesn't change dynamically is to use an HTMLPanel (and it's even easier with UiBinder).
If the structure is dynamic, then possibly make a ComplexPanel whose root element is a UListElement and which wraps all child widgets into a LIElement. Have a look at the internals of ComplexPanel, you'll see that it attaches and detaches the child widgets whenever they are added/removed while the panel itself is attached, or whenever the panel is attached / detached.
Related
I am trying to attach a widget in GWT to an element. I see this example,
How to add a custom widget to an element
but when I use HTMLPanel.wrap() I get an UmbrellaException. Exception is happening on this line:
assert Document.get().getBody().isOrHasChild(element);
How do I get my element to be attached to the DOM? Below is the code I am using:
#UiField
TableElement tblBootstrap;
public void addRow() {
TableRowElement row = tblBootstrap.insertRow(-1);
TableCellElement c = row.insertCell(-1);
HTMLPanel.wrap(c).add(new TextBox());
}
Many Thanks,
Eric
I'm using a GWT library (gwt-openlayers) which allows me to create a map popup containing arbitrary HTML, similar to Google Maps. I need this HTML to contain a GWT Button widget.
I'm creating some HTML elements on-the-fly like this:
Element outerDiv = DOM.createDiv();
outerDiv.getStyle().setOverflow(Overflow.HIDDEN);
outerDiv.getStyle().setWidth(100, Unit.PCT);
outerDiv.appendChild(new HTML(mapPOI.getHtmlDetails()).getElement());
Button popupButton = new Button("View Property");
popupButton.getElement().getStyle().setFloat(com.google.gwt.dom.client.Style.Float.RIGHT);
outerDiv.appendChild(popupButton.getElement());
Then I'm getting the source HTML for these elements by calling
String src = outerDiv.toString();
and inserting this html into my map marker. Now my map marker displays the content ok, including the button. However, the button won't respond to any events! From what I can gather, this is because the buttons onAttach() method is never being called.
Is there a better way to do this?
Thanks,
Jon
~~~~EDIT~~~~
I'm now trying a new way of doing this, which seems to be the accepted method looking at other similar posts.
First I'm creating my div:
String divId = "popup-" + ref;
String innerHTML = "<div id=\"" +divId + "\"></div>";
Then I'm adding this to my map popup and displaying it (which adds it to the DOM). After the popup has been displayed, I'm getting the Element as follows and trying to wrap a HTMLPanel around it:
Element element = Document.get().getElementById(divId);
HTMLPanel popupHTML = HTMLPanel.wrap(element);
My div element is successfully retrieved. However, HTMLPanel.wrap(element); doesn't complete. The reason for this is that wrap(..) calls RootPanel.detachOnWindowClose(Widget widget), which includes the following assertions:
assert !widgetsToDetach.contains(widget) : "detachOnUnload() called twice "
+ "for the same widget";
assert !isElementChildOfWidget(widget.getElement()) : "A widget that has "
+ "an existing parent widget may not be added to the detach list";
I put some breakpoints in and it seems that the 2nd assertion is failing!
Does anybody have any idea why this might be the case? Should failing this assertion really result in a complete failure of the method (no return)?
Your first approach is good, you just need to register onClick event for your button like this:
DOM.sinkEvents(popupButton.getElement(), Event.ONCLICK);
DOM.setEventListener(popupButton.getElement(), new EventListener() {
#Override
public void onBrowserEvent(Event event) {
//implement the logic after click
}
});
I have checked this, it works 100%!
You might try something like
RootPanel.get("idOfYourMapMarker").add(popupButton);
See RootPanel.get()
Unfortunately, RootPanels are AbsolutePanels which aren't so nice for layout but could work if you just have a simple button to add. You could also try RootLayoutPanel which will give you a LayoutPanel (also not so nice when you just want things to flow). You might end up creating a container widget that does the layout for you, and adding that to the RootPanel.
SimplePanel is a DIV. Perhaps that can be used instead?
You added the element, but you have to keep the hierarchy of the actual GWT Widgets too.
I don't see a clean way to do this, but you could use something like jQuery to grab the button by and ID and add a click handler back to it that would call the original click handler.
private static native void registerEvents(String buttonId, MyClass instance)/*-{
var $ = $wnd.$;
//check click
$('#'+buttonId).live('click', function(e) {
e.preventDefault();
instance.#com.package.MyClass::handleButtonClick(Lcom/google/gwt/event/dom/client/ClickEvent;)(null);
});
}-*/;
Call this registerEvents() either in your onAttach or constructor.
I once had a similar problem. You can use the gwt-openlayer's MapWidget as follows:
private MapWidget createMapWidget() {
final MapOptions defaultMapOptions = new MapOptions();
defaultMapOptions.setDisplayProjection(DEFAULT_PROJECTION);
defaultMapOptions.setNumZoomLevels(TOTAL_ZOOM_LEVELS);
MapWidget mapWidget = new MapWidget(MAP_WIDGET_WIDTH, MAP_WIDGET_HEIGHT, defaultMapOptions);
map = mapWidget.getMap();
return mapWidget;
}
And then add it to any panel be it vertical or horizontal.
MapWidget mapWgt = createMapWidget();
VerticalPanel mainPanel = new VerticalPanel();
mainPanel.add(mapWgt);
...
... add whatever you want
...
You can finally add the created Panel(containing the MapWidget and the gwt widget) to the PopupPanel. Also, you should now be able to add handlers to the gwt button.
I'm optimizing a GWT application that previously used a variety of nested panels to work with DIVs and Spans. I generate the entire table as a single SafeHtml object and then assigning it into a single SafeHtml widget.
I now want to be able to track mouseover/mouseout events at the level of the specific 'cell' spans rather than the entire table, but I'm not sure how to do this.
If I add a handler to the HTML widget itself, I'll get events sourced at various elements.
Since 2.0 there is quite a simple way to do it.
For example if you HTML code is contained in some kind of widget (HTMLPanel or HTML), you can calladdDomHandler(<handler>,<eventtyoe>) on that widget, so you will receive events from inner html.
For example if you have a bunch of anchors inside HTMLPanel and you want to know which one was clicked you can do something like this:
panel.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Element element= event.getNativeEvent().getEventTarget().cast();
if(element.getTagName().equals("A")) {
AnchorElement anchor = element.cast();
Window.alert("Anchor with href " + anchor.getHref() + " was clicked");
}
}
}, ClickEvent.getType());
Since you want to track mouseover/out events you will have to use 2 different dom handlers, find out cell you need when event is fired and then change its state.
The way to approach this is:
Find the element you need with one of the DOM methods, like DOM.getElementById(..) or any other means. View Widget.getElement() etc.
Call DOOM.sinkEvents(element,eventBits) or DOM.sinkBitlessEvent(element,eventName) and pass the required events you want to sink in form of a bitmask, like Event.MOUSEEVENTS or using a named event like click or touchstart if using the second method.
set and EventListener on the element, by calling DOM.setEventListerner(element,eventListener) like so:
DOM.setEventListener( element, new EventListener()
{
#Override
public void onBrowserEvent( Event event )
{
if ("click".event.getType()) {
// ..do stuff..
}
}
} );
Only events you've specified in step 2 will be fired to your EventListener, so you need to only handle those.
I'd like to use the following in UIBinder, so that I can programmatically set the href of the link in my code.
<g:HTMLPanel>
<g:Anchor ui:field="link">
<g:InlineLabel ui:field="firstName"/>
<g:InlineLabel ui:field="lastName"/>
</g:Anchor>
</g:HTMLPanel>
When I try this I get:
ERROR: Found widget in an HTML context Element <g:InlineLabel ui:field='firstName'> (:7).
How can I embed widgets inside an anchor? Previously I've resorted to using:
<a id="myAnchor">
etc...
</a>
And then manipulating the DOM in my code to set the HREF, but that's ugly. Is there a better way?
The class below acts exactly like a SimplePanel (i.e., you can put an widget in it), but uses an "a" instead of a "div". If you need more widgets just put another panel in it.
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.SimplePanel;
public class Link extends SimplePanel {
public Link() {
super(DOM.createAnchor());
}
private void setHref(String href) {
getElement().setAttribute("href", href);
}
private String getHref() {
return getElement().getAttribute("href");
}
public void setTarget(String frameName) {
getElement().setAttribute("target", frameName);
}
}
It is better to use a Panel (Flow or Horizontal) and add click handlers to the panel to simulate a link. Anchor, Button and similar widgets will not allow child tags inside them.
lets say i have a custom widget which has a ClickHandler. Here's the example:
public class TestWidget extends Composite {
private static TestWidgetUiBinder uiBinder = GWT
.create(TestWidgetUiBinder.class);
interface TestWidgetUiBinder extends UiBinder<Widget, TestWidget> {
}
#UiField
Button button;
public TestWidget(String firstName) {
initWidget(uiBinder.createAndBindUi(this));
button.setText(firstName);
}
#UiHandler("button")
void onClick(ClickEvent e) {
Window.alert("Hello!");
}
}
When i try to add this Widget like this:
TestWidget testWidget = new TestWidget("myTestWidget");
RootPanel.get().add(testWidget);
everything is fine. If i click on my button i get the message i expect.
However if i add it like this:
TestWidget testWidget = new TestWidget("myTestWidget");
RootPanel.getBodyElement().appendChild(testWidget.getElement());
my click event is not being fired. I'm struggeling to understand why.
It would be nice if someone could explain this to me or link me to an resource where i can read this up. Finally i would like to know if it is possible to add the clickhandler afterwards i appended the child event and if that way is recommended. Thanks it advance for help.
kuku
When you call add(), Widget.onAttach() is called on the widget that is being added to the panel. onAttach does some work to register the widget to receive events. appendChild() simply attaches one DOM element to another and does nothing else. You should be able to get events working in the second case by doing this:
Element element = testWidget.getElement();
RootPanel.getBodyElement().appendChild(element);
DOM.sinkEvents(element,
Event.getTypeInt(ClickEvent.getType().getName())
| DOM.getEventsSunk(element);
However, I haven't tested this and I wouldn't recommend that you use it in a real application. Using add() is definitely preferred, using appendChild() in this way has no advantages and may lead to unexpected behaviour.