Skip to content


Wicket Extensions - Anpassungen am ModalWindow

Wicket liefert im Bereich Extensions Komponenten mit, die zwar nicht direkt zum Kern von Wicket gehören, die aber fast so häufig wie die Standardwicketkomponenten zum Zuge kommen. Im folgenden Beitrag zeige ich, wie man die ModalWindow-Komponente benutzen kann und wie man die Darstellung an eigene Wünsche anpasst. Als erstes benötigen wir eine Komponente, die durch das ModalWindow angezeigt werden soll.

package de.wicketpraxis.web.blog.pages.questions.ajax.modal;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.panel.Panel;

public abstract class AbstractModalClosePanel extends Panel
{
  public AbstractModalClosePanel(ModalWindow modalWindow)
  {
    super(modalWindow.getContentId());
    
    add(new AjaxLink<Void>("link")
    {
      @Override
      public void onClick(AjaxRequestTarget target)
      {
        AbstractModalClosePanel.this.onClick(target);
      }
    });
  }
  
  public abstract void onClick(AjaxRequestTarget target);
}

Die ModalWindow-Komponente kann nur eine andere Komponente anzeigen. Daher ist die Komponenten-ID fest definiert. Daher macht es Sinn, statt einer eigenen ID einfach das ModalWindow, in dem die Komponente angezeigt werden soll als Parameter zu übergeben. Damit diese Komponente auch eine Funktion bereitstellt, fügen wir einen Link hinzu. Wichtig: Wenn man Formulare benutzen möchte, muss man diese bei Anzeige durch ein ModalWindow per Ajax absenden.

Das Markup der Komponente ist wenig überraschend:

<wicket:panel>
  <div>
    Nothing to hide:)
    <a wicket:id="link">Link</a>
  </div>
</wicket:panel>

Die ModalWindow-Komponente unterscheidet sich von anderen Komponenten in einem wesentlichen Punkt. Die Darstellung wird nicht durch ein Markup realisiert, dass man überschreiben könnte. Der Rahmen für das Fenster wird per Javascript erzeugt. Um das Aussehen der Komponente anzupassen müssen wir also etwas tiefer in die Trickkiste greifen.

In diesem Beispiel nehmen wir Veränderungen an verschiedenen Stellen vor. Fangen wir mit den Grafiken für den Rahmen an. Der Einfachheit halber verändere ich nur die Farbwirkung aber nicht die Dimensionen, so dass ich die vorhandenen Grafiken (die man sich aus dem wicket-extension.jar extrahieren kann) nur leicht verändere:

frame-custom-1-alphaframe-custom-1-ieframe-custom-2-alphaframe-custom-2-ie

Es gibt zwei Grafiken, wobei es für jede noch eine Version für den InternetExplorer gibt, die auf transparente Schatten verzichtet (der InternetExplorer hat da Schwierigkeiten, ähm.. nicht nur da). Wie man sieht befinden sich die Grafikinformationen für die Ränder, Ecken und Buttons in einer Grafik. Das Stylesheet sorgt dafür, dass an der richtigen Stelle die richtige Grafik eingeblendet wird. Daher müssen wir eine eigene Stylesheet-Datei einbinden, die unsere Anpassungen wiederspiegelt. Auch an dieser Stelle ist es sinnvoll, sich den Inhalt der ModalWindow-eigenen Stylesheet-Datei anzuschauen.

div.wicket-modal div.w_content_3
{
  border:0px;
}

div.wicket-modal div.w_caption
{
  background-color: inherit;
}

div.wicket-modal a.w_close {  
  top: 3px;
}

div.wicket-modal div.custom div.w_left,
div.wicket-modal div.custom div.w_right {
  background-image: url('frame-custom-2-alpha.png'); 
  _background-image: url('frame-custom-2-ie.png');
}


div.wicket-modal div.custom div.w_top,
div.wicket-modal div.custom div.w_bottom,
div.wicket-modal div.custom div.w_topLeft,
div.wicket-modal div.custom div.w_topRight,
div.wicket-modal div.custom div.w_bottomRight,
div.wicket-modal div.custom div.w_bottomLeft,
div.wicket-modal div.custom a.w_close {
  background-image: url('frame-custom-1-alpha.png'); 
  _background-image: url('frame-custom-1-ie.png');
}

Wie ich bereits weiter oben erwähnt habe, wird das Html für den Rahmen nicht wie üblich sondern per Javascript erzeugt. Da wir auch an dieser Stelle Anpassungen vornehmen wollen, müssen wir die entsprechende Funktion überschreiben:

Wicket.Window.getMarkup = function(idWindow, idClassElement, idCaption, idContent, idTop, idTopLeft, idTopRight, idLeft, idRight, idBottomLeft, idBottomRight, idBottom, idCaptionText, isFrame) {
  var s =
      "<div class=\"wicket-modal\" id=\""+idWindow+"\" style=\"top: 10px; left: 10px; width: 100px;\"><form style='background-color:transparent;padding:0px;margin:0px;border-width:0px;position:static'>"+
      "<div id=\""+idClassElement+"\">"+
        "<div class=\"w_caption\"  id=\""+idCaption+"\">"+
          "<a class=\"w_close\" href=\"#\"></a>"+                 
          "<span id=\""+idCaptionText+"\" class=\"w_captionText\"></span>"+
        "</div>"+
        
        "<div class=\"w_top_1\">"+

        "<div class=\"w_topLeft\" id=\""+idTopLeft+"\">"+
        "</div>"+       

        "<div class=\"w_topRight\" id=\""+idTopRight+"\">"+
        "</div>"+

        "<div class=\"w_top\" id='"+idTop+"'>"+                 
        "</div>"+

        "</div>"+
                
        "<div class=\"w_left\" id='"+idLeft+"'>"+
          "<div class=\"w_right_1\">"+
            "<div class=\"w_right\" id='"+idRight+"'>"+
              "<div class=\"w_content_1\" onmousedown=\"if (Wicket.Browser.isSafari()) { event.ignore = true; }  else { Wicket.stopEvent(event); } \">"+                                      
              
                "<div class=\"w_content_2\">"+
                "<div class=\"w_content_3\">"+
                  "<div class=\"w_content\">";
        if (isFrame) {
          if (Wicket.Browser.isIELessThan7() || !Wicket.Browser.isIE()) {                       
            s+= "<iframe src='\/\/:' frameborder=\"0\" id='"+idContent+"' allowtransparency=\"false\" style=\"height: 200px\">"+
                    "</iframe>";
          } else {
            s+= "<iframe src='about:blank' frameborder=\"0\" id='"+idContent+"' allowtransparency=\"false\" style=\"height: 200px\">"+
            "</iframe>";
          }
        } else {
          s+=
                    "<div id='"+idContent+"'></div>";
        }
          s+=             
                  "</div>"+
                "</div>"+
                "</div>"+
              "</div>"+
            "</div>"+
          "</div>"+
        "</div>"+


        "<div class=\"w_bottom_1\" id=\""+idBottom+"\">"+         
          
          "<div class=\"w_bottomRight\"  id=\""+idBottomRight+"\">"+
          "</div>"+
          
          "<div class=\"w_bottomLeft\" id=\""+idBottomLeft+"\">"+
          "</div>"+

          "<div class=\"w_bottom\" id=\""+idBottom+"\">"+       
          "</div>"+       


        "</div>"+       


      "</div>"+
    "</form></div>";
    
    return s;
}

Da die Javascript-Datei der ModalWindow-Komponente automatisch eingebunden wird (da von dieser Klasse abgeleitet wird), muss ich nur diese eine Funktion überschreiben. Ich habe mich in diesem Beispiel darauf beschränkt, den Bereich für die Überschrift und den CloseButton oberhalb des Rahmens darstellen zu lassen (Das div mit der Klasse "w_caption").

In unsere Komponentenklasse betreten wir nun wieder vertrautes Terrain. Wir leiten von ModalWindow ab und passen ein paar grundsätzliche Dinge an:

package de.wicketpraxis.web.blog.pages.questions.ajax.modal;

import org.apache.wicket.ResourceReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.JavascriptPackageResource;
import org.apache.wicket.markup.html.resources.CompressedResourceReference;
import org.apache.wicket.markup.html.resources.JavascriptResourceReference;

public class CustomModalWindow extends ModalWindow
{
  private static ResourceReference CSS = new CompressedResourceReference(CustomModalWindow.class, "styles/custom-modal.css");
  private static ResourceReference JAVASCRIPT = new JavascriptResourceReference(CustomModalWindow.class,  "styles/custom-modal.js");
  
  public CustomModalWindow(String id)
  {
    super(id);
    
    setCssClassName("custom");
    
    add(JavascriptPackageResource.getHeaderContribution(JAVASCRIPT));
    add(CSSPackageResource.getHeaderContribution(CSS));
    
    setCloseButtonCallback(new CloseButtonCallback()
    {
      public boolean onCloseButtonClicked(AjaxRequestTarget target)
      {
        return onCloseClicked(target);
      }
    });
    
    setWindowClosedCallback(new WindowClosedCallback()
    {
      public void onClose(AjaxRequestTarget target)
      {
        CustomModalWindow.this.onClose(target);
      }
    });
  }
  
  protected void onClose(AjaxRequestTarget target)
  {
    
  }

  protected boolean onCloseClicked(AjaxRequestTarget target)
  {
    return true;
  }
}

Wir setzen die Css-Klasse für den Rahmen, fügen unsere CSS- und Javascript-Anpassungen hinzu (da unsere Referenzen nach den ModalWindow-Referenzen eingebunden werden, überschreiben wir sowohl die CSS-Definitionen als auch die Javascript-Funktionen). Außerdem fügen wir zwei Callbacks hinzu, die auf einen Klick auf das Schließen-Symbol reagieren. Für diese Komponenten benötigen wir keine eigene Markup-Datei.

Jetzt möchten wir diese Komponente endlich verwenden. Dazu legen wir eine Seitenklasse an:

package de.wicketpraxis.web.blog.pages.questions.ajax.modal;

import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.util.time.Duration;

public class ModalWindowPage extends WebPage
{
  public ModalWindowPage()
  {
    final CustomModalWindow modalWindow = new CustomModalWindow("modal");
    
    modalWindow.setTitle("Popup");
    modalWindow.setInitialWidth(400);
    modalWindow.setInitialHeight(400);
    modalWindow.setResizable(true);
    modalWindow.setContent(new AbstractModalClosePanel(modalWindow)
    {
      @Override
      public void onClick(AjaxRequestTarget target)
      {
        modalWindow.close(target);
      }
    });
    add(modalWindow);
    
    add(new AjaxLink<Void>("open")
    {
      @Override
      public void onClick(AjaxRequestTarget target)
      {
        modalWindow.show(target);
      }
    });
    
    add(new AbstractAjaxTimerBehavior(Duration.seconds(2))
    {
      @Override
      protected void onTimer(AjaxRequestTarget target)
      {
        modalWindow.show(target);
        stop();
      }
    });
  }
}

Wir erstellen eine Instanz unsere ModalWindow-Komponente und fügen ein Panel als Content-Element hinzu. Dabei wird beim Klick auf den Link innerhalb der Komponente das Fenster geschlossen. Da das ModalWindow per Ajax geöffnet und geschlossen werden muss, benötigen wir zum Öffnen einen AjaxLink. Wenn man dem Nutzer das Fenster bereits beim Aufruf der Seite einmalig anzeigen möchte, kann man sich damit behelfen, das man eine Instanz AbstractAjaxTimerBehavior-Klasse erstellt und die Zeit entsprechend klein wählt (weniger als die zwei Sekunden aus diesem Beispiel). In der onTimer()-Methode führt der Aufruf von stop() dazu, dass die Methode nach der abgelaufenen Zeit nicht noch einmal aufgerufen wird.

<html>
  <head>
    <title>ModalWindow Page</title>
  </head>
  <body>
    <div wicket:id="modal"></div>
    <a wicket:id="open">Open</a>
  </body>
</html>

Und so sieht das Ergebnis aus:

wicket-custom-modalwindow

Der Aufwand für eine eigene Komponente ist zwar wesentlich höher als in anderen Fällen, aber der Aufwand lohnt schon deshalb, weil dann das Aussehen der ModalWindow-Kompoente zur restlichen Anwendung passt.

Tags:

Veröffentlicht in Allgemein, Technologie, Wicket, .