2009-07-16 4 views
11

Sto scrivendo un'app GWT che implica l'interazione con un documento esterno in un iframe. Come prova del concetto, sto cercando di collegare un gestore di clic a un pulsante.Collegamento di un evento GWT a un elemento in un iframe esterno

le seguenti opere in 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!"); 
}; 

Cercando di fare l'equivalente in GWT, ho fatto la seguente:

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

Quello che segue è la classe ElementWrapper:

public class ElementWrapper extends Widget implements HasClickHandlers { 

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

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


} 

Il codice per trovare il pulsante funziona correttamente ma non viene richiamato il gestore di eventi effettivo. Qualcuno ha avuto un problema simile prima e come lo hai risolto?

Grazie in anticipo,

Tin

risposta

10

Hilbrand ha ragione il problema è che il metodo GWT onAttach() non è stato chiamato.

ho implementato la soluzione originale, aggiungendo il seguente metodo per ElementWrapper:

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

E chiamato aggiunto wrapper.onAttach() dopo la ElementWrapper viene creato. Funziona come un fascino!

+0

Grazie! Sapevo che doveva esserci un modo più pulito :) – triggerNZ

0

Si potrebbe utilizzare JSNI di riutilizzare il vostro pezzo JavaScript del codice. Il tuo codice javascript chiamerebbe un metodo gwt su un oggetto che lo lancerebbe per conto del pulsante nell'iframe.

Per quanto riguarda il motivo per cui il codice GWT non funziona, suppongo che sia perché utilizzano un certo livello in cima ai normali eventi del browser che probabilmente non possono occupare più di 1 fotogramma. Questa è solo un'ipotesi. Puoi archiviare questo come una funzione/richiesta di bug di nuovo il team GWT. Se ho ragione il tuo codice sembra a posto.

+0

Cool, grazie per la risposta. Immagino che esplorerò il percorso JSNI, anche se voglio stare lontano dalla scrittura di javascript personalizzati il ​​più possibile. – triggerNZ

1

Dopo aver cercato ulteriormente, ho scoperto che l'iframe è irrilevante. Lo stesso comportamento non funziona su un pulsante normale nella pagina host.

Fondamentalmente l'ho risolto utilizzando JSNI per replicare parte del meccanismo di gestione degli eventi di GWT. I seguenti lavori:

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

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

Infine, il ClickEventManager, dove il lavoro effettivo accade è:

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 odio questa soluzione perché appaio duplicare la gestione degli eventi di GWT e molto probabilmente l'introduzione di brutte perdite di memoria javascript. Qualche idea sul perché il mio primo post non funzioni (ricordando che l'aspetto iframe è un'aringa rossa), sarebbe apprezzato.

Grazie,

Tin

+0

Grazie, grazie! Questo era esattamente ciò di cui avevo bisogno per farlo funzionare in IE7! – SorcyCat

4

mi aspetto che il problema è che il metodo GWT onAttach() non viene chiamato quando si utilizza l'involucro come nel tuo primo esempio. Puoi provare a utilizzare il metodo statico wrap nel widget Button. Sebbene per utilizzarlo, lo input deve essere di tipo button. O dare un'occhiata all'implementazione del metodo wrap.Ecco il codice modificato quando si utilizza il metodo wrap:

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

Vedere la mia risposta precedente. Una leggera modifica alla tua soluzione originale lo farà funzionare.

1

Potete trovare questo utile:

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

Sarete quindi in grado di realizzare contenitori arbitrarie in Widget contenitori:

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

E associare eventi a elementi arbitrari:

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

In particolare nel tuo caso, prova questo:

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

Mi ha sempre confessato che GWT rende questo compito così difficile e scarsamente documentato.

1

Invece di utilizzare iframe, suggerisco di effettuare semplicemente una richiesta http da GWT tramite com.google.gwt.http.client.RequestBuilder. Così:

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