Присоединение события GWT к элементу во внешнем iframe
Я пишу приложение GWT, которое предполагает взаимодействие с внешним документом в iframe. В качестве доказательства концепции я пытаюсь привязать обработчик кликов к кнопке.
Следующие работы в 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());
}
}
Код для поиска кнопки работает нормально, но обработчик события клика не получает вызова. Раньше у кого-то была аналогичная проблема, и как вы ее разрешили?
Спасибо заранее,
Олово
Ответы
Ответ 1
Хильбранд прав насчет того, что метод GWT onAttach()
не был вызван.
Я внедрил ваше оригинальное решение, добавив следующий метод в ElementWrapper
:
public void onAttach() {
super.onAttach();
}
И вызвал добавленный wrapper.onAttach()
после создания ElementWrapper
. Работает как шарм!
Ответ 2
Я ожидаю, что проблема заключается в том, что метод GWT onAttach()
не вызывается, когда вы используете упаковку, как в первом примере. Вы можете попробовать использовать статический метод wrap
для виджета Button. Хотя для использования этого параметра input
должен иметь тип button
. Или посмотрите на реализацию метода wrap
. Вот модифицированный код при использовании метода wrap
:
Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
Button button = Button.wrap(buttonElement);
HandlerRegistration handlerRegistration = button.addClickHandler(clickHandler);
Ответ 3
После изучения этого далее я обнаружил, что 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 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()();
}
}-*/;
}
Лично я ненавижу это решение, потому что я, кажется, дублирую обработку событий GWT и, вполне возможно, вводя неприятные утечки памяти в JavaScript. Любые идеи о том, почему мой первый пост не работает (помня, что аспект iframe является красной селедкой), будет оценено.
Спасибо,
Олово
Ответ 4
Вы можете счесть это полезным:
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 делает это настолько трудным и плохо документированным.
Ответ 5
Вместо использования iframe я предлагаю вам просто сделать HTTP-запрос от GWT через com.google.gwt.http.client.RequestBuilder. Например:
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);
}
}
Ответ 6
Вы можете использовать JSNI для повторного использования кода JavaScript. Ваш javascript-код вызовет метод gwt для объекта, который будет отбрасывать его от имени кнопки в iframe.
Что касается того, почему код GWT не работает, я думаю, это потому, что они используют некоторый слой поверх обычных событий браузера, которые, вероятно, не могут охватывать более одного кадра. Впрочем, это только предположение. Вы можете указать это как запрос функции/ошибки снова в команде GWT. Если я прав, ваш код выглядит просто.
Ответ 7
См. мой предыдущий ответ. Небольшая модификация вашего оригинального решения заставит его работать.