我正在编写一个涉及与iframe中的外部文档进行交互的GWT应用程序。作为概念证明,我试图将一个点击处理程序附加到一个按钮。
以下适用于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!");
};
尝试在GWT中执行等效操作,我做了以下操作:
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;
}-*/;
以下是ElementWrapper类:
public class ElementWrapper extends Widget implements HasClickHandlers {
public ElementWrapper(Element theElement) {
setElement(theElement);
}
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
}
查找按钮的代码工作正常,但未调用实际的单击事件处理程序。以前是否有人遇到类似的问题,你是如何解决的?
提前致谢,
锡
答案 0 :(得分:10)
Hilbrand对于没有调用GWT方法onAttach()
的问题是正确的。
我实施了原始解决方案,将以下方法添加到ElementWrapper
:
public void onAttach() {
super.onAttach();
}
在创建wrapper.onAttach()
后调用已添加ElementWrapper
。像魅力一样工作!
答案 1 :(得分:4)
我预计问题在于,当您使用第一个示例中的包装时,不会调用GWT方法onAttach()
。您可以尝试在Button小部件上使用静态wrap
方法。虽然要使用此input
,但button
必须为wrap
。或者看一下wrap
方法的实现。以下是使用Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
Button button = Button.wrap(buttonElement);
HandlerRegistration handlerRegistration = button.addClickHandler(clickHandler);
方法时修改后的代码:
{{1}}
答案 2 :(得分:1)
在进一步研究之后,我发现iframe无关紧要。相同的行为不适用于主机页面上的普通按钮。
我基本上通过使用JSNI来复制GWT的事件处理机制的一部分来修复它。以下作品:
Element buttonElement = DOM.getElementById("externalButton");
new CustomElementWrapper(buttonElement).addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Window.alert("GWT hooked into button");
}
});
CustomElementWrapper的位置是:
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();
}
}
最后,实际工作发生的ClickEventManager是:
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;
cm.@com.talktactics.agent2.client.widgets.ClickEventManager::invokeClickHandler()();
}
}-*/;
}
就个人而言,我讨厌这个解决方案,因为我似乎在复制GWT的事件处理,并且很可能引入讨厌的javascript内存泄漏。关于为什么我的第一篇文章不起作用的任何想法(记住iframe方面是一个红色的鲱鱼),将不胜感激。
谢谢,
锡
答案 3 :(得分:1)
您可能会觉得这很有用:
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());
}
}
然后,您就可以将任意容器放入窗口小部件容器中:
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);
将事件绑定到任意元素:
Element root = Document.get().getElementById("target");
DirectPanel p = new DirectPanel(root);
p.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// ...
}
});
特别是在你的情况下,试试这个:
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
}
});
}
总是让我感到困惑的是,GWT使得这样做非常困难并且记录不完整。
答案 4 :(得分:1)
我建议你只需通过com.google.gwt.http.client.RequestBuilder从GWT发出http请求,而不是使用iframe。像这样:
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);
}
}
答案 5 :(得分:0)
您可以使用JSNI重用JavaScript代码段。你的javascript代码会在一个对象上调用gwt方法,该方法会代表iframe中的按钮抛出它。
至于为什么GWT代码不起作用 - 我想这是因为他们在常规浏览器事件之上使用了一些层,这些事件可能不会超过1帧。这只是一个猜测。您可以将此作为功能/错误请求再次归档为GWT团队。如果我是对的,你的代码看起来很好。
答案 6 :(得分:0)
请参阅我之前的回答。对原始解决方案稍作修改即可使其正常工作。