Podłączenie zdarzenia GWT do elementu w zewnętrznej ramce iframe
Piszę aplikację GWT, która polega na interakcji z zewnętrznym dokumentem w ramce iframe. Jako dowód koncepcji próbuję dołączyć obsługę kliknięć do przycisku.
Następujące działania w javascript
var iframe = document.getElementById("rawJSIFrame");
var doc = iframe.contentDocument;
var body = doc.body;
var button = doc.getElementsByTagName("input").namedItem("submit");
button.onclick = function() {
alert("Clicked!");
};
Próbując zrobić odpowiednik w GWT, zrobiłem co następuje:
public void addClickHandlerToSubmitButton(String buttonElementName, ClickHandler clickHandler) {
IFrameElement iframe = IFrameElement.as(frame.getElement());
Document frameDocument = getIFrameDocument(iframe);
if (frameDocument != null) {
Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
ElementWrapper wrapper = new ElementWrapper(buttonElement);
HandlerRegistration handlerRegistration = wrapper.addClickHandler(clickHandler);
}
}
private native Document getIFrameDocument(IFrameElement iframe)/*-{
return iframe.contentDocument;
}-*/;
Poniżej znajduje się Klasa ElementWrapper:
public class ElementWrapper extends Widget implements HasClickHandlers {
public ElementWrapper(Element theElement) {
setElement(theElement);
}
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
}
Kod do znalezienia przycisku działa dobrze, ale rzeczywisty mechanizm obsługi zdarzenia kliknięcia nie jest wywoływany. Czy ktoś miał podobny problem wcześniej i jak go rozwiązałeś?
Z góry dzięki,
Tin
7 answers
Hilbrand ma rację co do problemu polegającego na tym, że metoda GWT onAttach()
nie została wywołana.
Zaimplementowałem twoje oryginalne rozwiązanie, dodając do ElementWrapper
następującą metodę:
public void onAttach() {
super.onAttach();
}
I wywołane dodane wrapper.onAttach()
Po utworzeniu ElementWrapper
. Działa jak urok!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-06 19:33:11
Spodziewam się, że problem polega na tym, że metoda GWT onAttach()
nie jest wywoływana, gdy używasz owijania, jak w pierwszym przykładzie. Możesz spróbować użyć metody static wrap
w widżecie przycisku. Chociaż aby tego użyć, input
musi być typu button
. Lub przyjrzyj się implementacji metody wrap
. Oto zmodyfikowany kod przy użyciu metody wrap
:
Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
Button button = Button.wrap(buttonElement);
HandlerRegistration handlerRegistration = button.addClickHandler(clickHandler);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-22 10:00:08
Po zbadaniu tego dalej, odkryłem, że ramka iframe jest nieistotna. To samo zachowanie nie działa na zwykłym przycisku na stronie hosta.
W zasadzie naprawiłem to używając JSNI do replikacji części mechanizmu obsługi zdarzeń GWT. Następujące utwory:
Element buttonElement = DOM.getElementById("externalButton");
new CustomElementWrapper(buttonElement).addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Window.alert("GWT hooked into button");
}
});
Gdzie CustomElementWrapper jest:
public class CustomElementWrapper extends Widget implements HasClickHandlers {
private ClickEventManager clickEventManager;
public CustomElementWrapper(Element theElement) {
setElement(theElement);
clickEventManager = new ClickEventManager(theElement);
}
public HandlerRegistration addClickHandler(ClickHandler handler) {
//The 'right' way of doing this would be the code below. However, this doesn't work
// A bug in GWT?
//
// return addDomHandler(handler, ClickEvent.getType());
return clickEventManager.registerClickHandler(handler);
}
void invokeClickHandler() {
clickEventManager.invokeClickHandler();
}
public boolean isClickHandlerRegistered() {
return clickEventManager.isClickHandlerRegistered();
}
}
Wreszcie, Menedżer ClickEventManager, w którym dzieje się rzeczywista praca, to:
public class ClickEventManager {
private boolean clickHandlerRegistered = false;
private ClickHandler clickHandler;
private Element element;
public ClickEventManager(Element element) {
this.element = element;
}
public void invokeClickHandler() {
//This shouldn't really be null but we are bypassing GWT's native event mechanism
//so we can't create an event
clickHandler.onClick(null);
}
public boolean isClickHandlerRegistered() {
return clickHandlerRegistered;
}
HandlerRegistration registerClickHandler(ClickHandler handler) {
clickHandler = handler;
if (!clickHandlerRegistered) {
registerClickHandlerInJS(element);
clickHandlerRegistered = true;
}
return new HandlerRegistration() {
public void removeHandler() {
//For now, we don't support the removal of handlers
throw new UnsupportedOperationException();
}
};
}
private native void registerClickHandlerInJS(Element element)/*-{
element.__clickManager = this;
element.onclick
= function() {
var cm = this.__clickManager;
[email protected]::invokeClickHandler()();
}
}-*/;
}
Osobiście nienawidzę tego rozwiązania, ponieważ wydaje mi się, że powielam Zdarzenie GWT obsługa i całkiem możliwe wprowadzenie paskudnych wycieków pamięci javascript. Wszelkie pomysły na to, dlaczego mój pierwszy post nie działa (pamiętając, że aspekt iframe jest czerwonym śledziem), będą mile widziane.
Dzięki,
Tin
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 22:20:25
Może Ci się to przydać:
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.AbsolutePanel;
public class DirectPanel extends AbsolutePanel implements HasClickHandlers {
public DirectPanel(Element elem) {
super(elem.<com.google.gwt.user.client.Element> cast());
onAttach();
}
@Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
}
Wtedy będziesz mógł zrobić dowolne kontenery w kontenery widgetów:
Element root = Document.get().getElementById("target");
DirectPanel p = new DirectPanel(root);
Button register = new Button("Register");
register.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// ...
}
});
p.add(register);
I powiązać zdarzenia z dowolnymi elementami:
Element root = Document.get().getElementById("target");
DirectPanel p = new DirectPanel(root);
p.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// ...
}
});
Szczególnie w Twoim przypadku, spróbuj tego:
IFrameElement frm = Document.get().createIFrameElement();
Document d = frm.getContentDocument();
NodeList<Element> inputs = d.getElementsByTagName("input");
InputElement target = null;
for(int i = 0; i < inputs.getLength(); ++i) {
Element e = inputs.getItem(0);
if (e.getNodeName().equals("submit")) {
target = InputElement.as(e);
break;
}
}
if (target != null) {
DirectPanel p = new DirectPanel(target);
p.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// TODO Auto-generated method stub
}
});
}
Zawsze mnie zadziwiało, że GWT sprawia, że robienie tego jest tak trudne i słabo udokumentowane.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-29 08:36:25
Zamiast używać iframes proponuję po prostu złożyć żądanie http z GWT przez com.google.gwt.http.klient.RequestBuilder. Tak:
private void getHtml(String url) {
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
rb.setCallback(new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
HTMLPanel html = new HTMLPanel(response.getText());
// Now you have a widget with the requested page
// thus you may do whatever you want with it.
}
@Override
public void onError(Request request, Throwable exception) {
Log.error("error " + exception);
}
});
try {
rb.send();
} catch (RequestException e) {
Log.error("error " + e);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-03 18:36:40
Możesz użyć JSNI do ponownego użycia kodu JavaScript. Twój kod javascript wywoła metodę gwt na obiekcie, który rzuci ją w imieniu przycisku w ramce iframe.
Co do tego, dlaczego kod GWT nie działa -- chyba dlatego, że używają jakiejś warstwy na zwykłych zdarzeniach przeglądarki, które prawdopodobnie nie mogą rozciągać się na więcej niż 1 klatkę. Tylko zgaduję. Możesz to zgłosić jako żądanie funkcji / błędu przeciwko zespołowi GWT. Jeśli mam rację, Twój kod wygląda dobrze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-17 14:22:20
Zobacz moją poprzednią odpowiedź. Niewielka modyfikacja oryginalnego rozwiązania sprawi, że będzie działać.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-06 19:54:03