2009-07-16 13 views
11

Estoy escribiendo una aplicación GWT que implica interactuar con un documento externo en un iframe. Como una prueba de concepto, estoy tratando de adjuntar un controlador de clic a un botón.Enlazando un evento GWT a un elemento en un iframe externo

las siguientes obras en 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!"); 
}; 

Tratando de hacer el equivalente en GWT, hice lo siguiente:

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; 
}-*/; 

La siguiente es la clase ElementWrapper:

public class ElementWrapper extends Widget implements HasClickHandlers { 

    public ElementWrapper(Element theElement) { 
     setElement(theElement); 
    } 

    public HandlerRegistration addClickHandler(ClickHandler handler) { 
     return addDomHandler(handler, ClickEvent.getType()); 
    } 


} 

El código para encontrar el botón funciona bien, pero el controlador de evento click real no se invoca. Alguien ha tenido un problema similar anteriormente, y ¿cómo lo resolvió?

Gracias de antemano,

estaño

Respuesta

10

Hilbrand tiene razón sobre el problema es que el método GWT onAttach() no fue llamado.

he implementado su solución original, añadiendo el siguiente método para ElementWrapper:

public void onAttach() { 
    super.onAttach(); 
    } 

Y añade wrapper.onAttach() llamada después de la ElementWrapper se crea. ¡Funciona de maravilla!

+0

¡Gracias! Sabía que tenía que haber una manera más limpia :) – triggerNZ

0

Usted podría utilizar JSNI volver a utilizar su pieza de código JavaScript. Su código javascript llamaría a un método gwt en un objeto que lo arrojaría en nombre del botón en el iframe.

En cuanto a por qué el código GWT no funciona, supongo que es porque usan alguna capa encima de los eventos normales del navegador que probablemente no pueden abarcar más de 1 fotograma. Sin embargo, eso solo es una suposición. Puede presentar esto como una solicitud de función/error contra el equipo de GWT. Si estoy en lo cierto, tu código se ve bien.

+0

Cool, gracias por la respuesta. Supongo que exploraré la ruta JSNI, aunque quiero evitar escribir javascript personalizados tanto como sea posible. – triggerNZ

1

Después de investigar esto más, descubrí que el iframe es irrelevante. El mismo comportamiento no funciona en un botón normal en la página de host.

Básicamente lo solucioné usando JSNI para replicar parte del mecanismo de manejo de eventos de GWT. Las siguientes obras:

Element buttonElement = DOM.getElementById("externalButton"); 
new CustomElementWrapper(buttonElement).addClickHandler(new ClickHandler() { 
public void onClick(ClickEvent event) { 
     Window.alert("GWT hooked into button"); 
    } 
}); 

Dónde CustomElementWrapper es:

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(); 
    } 
} 

Por último, el ClickEventManager, donde ocurre el trabajo real es:

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()(); 
     } 
}-*/; 
} 

Personalmente, no me gusta esta solución porque aparezco estar duplicando el manejo de eventos de GWT y muy posiblemente introduciendo desagradables pérdidas de memoria de JavaScript. Cualquier idea sobre por qué mi primera publicación no funciona (recordando que el aspecto iframe es una pista falsa), sería apreciada.

Gracias,

estaño

+0

¡Gracias, gracias! ¡Esto era exactamente lo que necesitaba para que funcionara en IE7! – SorcyCat

4

que esperan que el problema es que el método GWT onAttach() no se llama cuando se utiliza el embalaje como en el primer ejemplo. Puede intentar utilizar el método estático wrap en el widget Botón. Aunque para usar esto, el input debe ser del tipo button. O eche un vistazo a la implementación del método wrap.Aquí está el código modificado cuando se utiliza el método de wrap:

Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne(); 
Button button = Button.wrap(buttonElement); 
HandlerRegistration handlerRegistration = button.addClickHandler(clickHandler); 
0

Por favor, consulte mi respuesta anterior. Una ligera modificación a su solución original lo hará funcionar.

1

Usted puede encontrar útil esta información:

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()); 
     } 
} 

A continuación, será capaz de hacer los envases arbitrarias en contenedores de widgets:

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); 

Y enlazar eventos a elementos arbitrarios:

Element root = Document.get().getElementById("target"); 
    DirectPanel p = new DirectPanel(root); 
    p.addClickHandler(new ClickHandler() { 
    @Override 
    public void onClick(ClickEvent event) { 
     // ... 
    } 
    }); 

Específicamente en su caso, intente esto:

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 
    } 
    }); 
} 

Siempre me ha desconcertado que GWT haga esto tan difícil y mal documentado.

1

En lugar de usar iframes, le sugiero que simplemente realice una solicitud HTTP desde GWT a través de com.google.gwt.http.client.RequestBuilder. Como tal:

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); 
     } 
    } 
Cuestiones relacionadas