Oca 26

Apache Wicket ile Dialogbox Oluşturma

Sal 26 Oca 2010 17:24:44 | 2 yorum


Taner Diler
taner.diler[et]gmail.com


Bileşen bazı web çatısı (Component Based Web Framework) olan Apache Wicket ile kendimize ait bileşenler oluşturmak çok kolay. Web uygulamalarımızda çok kullandığımız kullanıcı yönlendirici mesajları göstermek için bir Wicket Bileşeni nasıl oluştururuz gelin hep birlikte görelim.
[--split--]

Apache Wicket ile Bileşen Tabanlı (Component Based) Web uygulaması gerçekleştirmek kolay ve zevkli bir iş haline geliyor. Bir iki klavye hamlesi ile AJAX uygulaması yazabilirsiniz.

Uygulamalarda kullanıcı bilgilendirme mesajları kimi zaman ilgili formun üzerinde, form girişlerinin yanında ve ya ayrı sayfalarda gösterilmektedir. Bu mesajların gösterimindeki görsel uyumluluk kullanıcının mesajı okumadan içeriği hakkında bilgi sahibi olmasını sağlar. Yönlendirme düğmeleri (button) kullanılarak hangi işleme devam etmesi seçenek olarak sunulur.

Aşağıdaki gibi bir mesaj kutusu tasarladım.




1. SUCCESS, INFO, WARNING, ERROR ve UNAUTHORIZED şeklinde 5 tip mesaj gösterebiliriz.
2. Her mesaj tipinin kendisine ait logosu olacak.
3. Bazı genel mesajlarda title ve footer alanı olmayacak.
4. İstediğim kadar yönlendirme düğmesi ekleyebilmeliyim.

Çizmiş olduğumuz arayüz tasarımını, tasarımcıya verip HTML ve CSS kodlamalarını gerçekleştirelim.

HTML kodları içerisinde Apache Wicket bileşenlerine wicket:id eklemesini yapalım.



Wicket bileşenleri:
 
         1. Mesaj Logosu
         2. Mesaj Başlık Panel’i
         3. Mesaj Başlığı (Title)
         4. Mesaj Gövdesi (Body)
         5. Mesaj Sonu (Footer)
         6. Yönlendirme Düğmeleri (Buttons)

<wicket:panel>
<div class="msg_container">
<div class="msg_body">
<table border="0" cellpadding="0" cellspacing="0">
      <tr>
            <td width="160"><img wicket:id="logo"/></td>
            <td><div wicket:id="titlePanel"></div>
            <br /><span wicket:id="body">[body]</span><br />
            <br /><span wicket:id="footer">[footer]</span><br />
            <br />
            <br />
                  <div style=" margin-right: 30px;">
                     <div wicket:id="buttons" style="float:right; margin-right:15px;" >
                       <input type="button" wicket:id="button"/>
                   </div>
                </div>
                  <div style="clean: both"/>
            </td>
      </tr>
</table>
</div>
</div>
</wicket:panel>

Neden mesaj başlığını bir panel içerisine koyuyoruz? Cevabı basit, başlığı olmayan bir mesaj göstermek istediğimde gövdenin yukarısında göze batacak kadar boşluk olmasını istemiyorum. Bu yüzden burada bulunan div’i gizlemem gerek. Bunun için de başlığın bulunduğu div’e bir wicket panel’i olarak işlem yapacağım.

<wicket:panel>
<strong style="color: #007087;">
<span wicket:id="title">[title]</span>
</strong><br />
</wicket:panel>


UML Sınıf Diyagramım aşağıdaki gibi olacaktır:




Gösterilecek olan başlık anahtarını, içerik anahtarını (ResourceBundle Key), düğmeleri taşıyacak Message sınıfı oluşturdum.


public class Message implements Serializable {
      private MessageType type;
      private String bodyKey;
      private String footerKey;
      private List buttons = new ArrayList();
      
      public Message(MessageType type, String titleKey, String bodyKey,
                  String footerKey) {
            this.type = type;
            this.titleKey = titleKey;
            this.bodyKey = bodyKey;
            this.footerKey = footerKey;
      }
      
      public void addButton(MessageButton button){
           this.buttons.add(button);
      }
      ...
}

Gösterilecek olan mesajın tipine göre logo değişimi olacaktır. Bu yüzden resim tanımlarını mesaj tipi ile ilişkilendirdim. Bunun için Enum yapısını kullanabiliriz. Eğer renk farklılığı da olması istenirse CSS tanımlarını da burada kullanabiliriz.

public enum MessageType {
      ERROR(ConfirmMessagePanel.ERROR_IMAGE),
      INFO(ConfirmMessagePanel.INFO_IMAGE),
      SUCCESS(ConfirmMessagePanel.SUCCESS_IMAGE),
      WARNING(ConfirmMessagePanel.WARNING_IMAGE),
      DENIED(ConfirmMessagePanel.DENIED_IMAGE);
      
      private ResourceReference image;
      
      private MessageType(ResourceReference image){
            this.image = image;     
      }
      
      public ResourceReference getImage(){
            return this.image;
      }
}

MessageButton soyut sınıfından oluşturacağımız her nesnenin kendine özel davranışı olacaktır. Bu yüzden soyut clickButton yordamı tanımladık. AjaxLink Wicket yapısını kullanarak butonların HTML Button’u olarak işlem görmesini Form Submit Button’u olarak tanımlanmamasını sağlarız. Parametre olarak aldığımız AjaxRequestTarget nesnesinin addCompenent() yordamını kullanarak bize Ajax Call’u sonunda hangi bileşenlerin yeniden yorumlanacağını (rendering) ve yeni HTML kodu üretileceğini belirleriz.

public abstract class MessageButton extends AjaxLink {
      public MessageButton(String labelKey) {
            super("button", new Model(labelKey));
            this.add(new SimpleAttributeModifier("value",
                            new ResourceModel(labelKey).getObject()));
      }
 
      public abstract void clickButton(AjaxRequestTarget target);
 
      @Override
      public void onClick(AjaxRequestTarget target) {
            this.clickButton(target);
      }
}

Gelelim mesajlarımızı ve diğer görsel bileşenleri Wicket Panel ile göstermeye. Bunun için Panel Wicket yapısından türemiş MessagePanel sınıfını oluşturmalıyız.

public class MessagePanel extends Panel implements IHeaderContributor {

    private static final long serialVersionUID = -3432489401302616102L;

    private ConfirmMessageModal modal;

    /** reference to the confirmbox's css resource */
    private static final ResourceReference CSS = new ResourceReference(
            MessagePanel.class, "confirmbox.css");

    /** reference to WARNING image */
    static final ResourceReference WARNING_IMAGE = new ResourceReference(
            MessagePanel.class, "WARNING.gif");

    /** reference to INFO image */
    static final ResourceReference INFO_IMAGE = new ResourceReference(
            MessagePanel.class, "INFO.gif");

    /** reference to ERROR image */
    static final ResourceReference ERROR_IMAGE = new ResourceReference(
            MessagePanel.class, "ERROR.gif");

    /** reference to SUCCESS image */
    static final ResourceReference SUCCESS_IMAGE = new ResourceReference(
            MessagePanel.class, "SUCCESS.gif");

    /** reference to DENIED image */
    static final ResourceReference DENIED_IMAGE = new ResourceReference(
            MessagePanel.class, "DENIED.gif");

    /*
     * (non-Javadoc)
     *
     * @see
     * org.apache.wicket.markup.html.IHeaderContributor#renderHead(org.apache
     * .wicket.markup.html.IHeaderResponse)
     */
    public void renderHead(IHeaderResponse response) {
        response.renderCSSReference(CSS);

    }

    public MessagePanel(String id, Message message) {
        this(id, message, false);
    }

    public MessagePanel(String id, Message message, boolean hideTitle) {
        super(id);

        TitlePanel titlePanel = new TitlePanel("titlePanel", new ResourceModel(
                message.getTitleKey(), message.getBodyKey()));

        add(new Image("logo", message.getMessageType().getImage()));
        add(titlePanel);

        add(new Label("body", new ResourceModel(message.getBodyKey(), message
                .getBodyKey())));

        add(new Label("footer", new ResourceModel(message.getFooterKey(), "")));
        add(new ListView("buttons", message.getButtonList()) {

            private static final long serialVersionUID = 3339539327149648078L;

            @Override
            protected void populateItem(ListItem item) {
                final MessageButton button = item.getModelObject();
                item.add(button);
            }
        });

        if (hideTitle) {
            titlePanel.setVisible(false);
        }
    }

    void setModal(ConfirmMessageModal modal) {
        this.modal = modal;
    }

    ConfirmMessageModal getModal() {
        return this.modal;
    }

    private class TitlePanel extends Panel {
        private static final long serialVersionUID = -1766112576379965369L;

        public TitlePanel(String id, IModel model) {
            super(id, model);
            add(new Label("title", model));
        }
    }

}


Yukarıda gördüğümüz gibi MessagePanel sınıfı IHeaderContributor arayüzünü gerçekleştirmiş. Eğer bir Wicket Bileşen’inin Header’da değişiklik yapmasını istiyorsak bu arayüzün renderHead() yordamını gerçekleştirmesini sağlamalıyız. CSS ve resim dosyalarının MessagePanel ile paketlemek istediğimiz için aynı dizinde bulunmalıdırlar. Wicket tarafından kullanılabilir olmaları için herbirine ait ResourceReference nesnesi oluşturuyoruz.

Mesaj kutusunun inşaası yapılandırıcı içerisinde gerçekleşmekte. Yapılandırıcı dışında onBeforeRender() yordamı içerisinde de bunu gerçekleştirebiliriz.

Yapılandırıcı içerisinde liste içerisindeki MessageButton’ları göstermek için ListView Wicket yapısını kullanıyoruz. Bu yapı da bir wicket html tanımına ihtiyaç duymaktadır.

<div wicket:id="buttons" style="float:right; margin-right:15px;" >
      <input type="button" wicket:id="button"/>
</div>

Önceki paragraflarda title’in isteğimize göre görünmesini/görünmemesini bunu ayrı bir Wicket Panel gibi düşünerek sağlayabiliriz demiştik. Yapılandırı içerisindeki son satırlarda göndermiş olduğumuz hideTitle parametresine göre panelin setVisible(false) yordamını çağırıyoruz. Başlığı içerecek kısmı panel olarak tanımladığımız için buna ait Wicket Panel sınıfı ve html dosyası oluşturmamız gerekmektedir. Encapsulation prensiplerine uyarak bu panel sınıf tanımını private erişim belirtecine sahip Inner Class olarak yapalım.

public class MessagePanel extends Panel implements IHeaderContributor {

    ...

    private class TitlePanel extends Panel {
        ...
        public TitlePanel(String id, IModel model) {
            super(id, model);
            add(new Label("title", model));
        }
    }

}

Wicket Html tanımı ise ConfirmMessagePanel$TitlePanel.html dosyası içerisinde bulunmalı. Dikkat edildiği gibi derleme sonrasında oluşacak inner class dosya ismi ile aynı.

<wicket:panel>
<strong style="color: #007087;"><span   wicket:id="title">[title]</span></strong>
<br />
</wicket:panel>


Oluşak olan uygulama dizin yapısı aşağıdaki gibidir:



Uygulama içerisinde MessagePanel’imizi nasıl kullanacağız? İşte burada bir örnek mevcut:


...
Message message = new Message(MessageType.INFO,
                "message.panel.title",
                "message.panel.body",
                "message.panel.footer");
message.addButton(new MessageButton("message.button.no"){
      ...
    @Override
    public void clickButton(AjaxRequestTarget target) {
        // DO NOTHING
    }
});
message.addButton(new MessageButton("message.button.yes"){
    ...
    @Override
    public void clickButton(AjaxRequestTarget target) {
        // DO NOTHING
    }
});
MessagePanel messagePanel = new MessagePanel("content", message);
add(messagePanel);
...

Sonuç :



Mesajlarımızın ModalBox içerisinde görünmesini istiyorsak

Apache Wicket ile modal pencereleri yapmak çok kolay. ModalWindow Wicket yapısından yeni yapılar türeterek bunu gerçekleştirebiliriz.

MessagePanel nesnesini Modal özelliği katmak için sarmalayıcı bir sınıf oluşturalım. MessageModal, ModalWindow’dan türeyen bir yapı.

public class MessageModal extends ModalWindow {

    public MessageModal(String id, Message message) {
        super(id);
        this.setMinimalHeight(176);
        this.setInitialHeight(176);
        MessagePanel panel = new MessagePanel(this.getContentId(), message, true);
        panel.setModal(this);
        this.setTitle(new ResourceModel(message.getTitleKey()));
        setContent(panel);
    }


    public void showMessage(AjaxRequestTarget target) {
        this.show(target);
    }

}


MessageModal ile gösterilen mesajlarda başlıklar pencerenin başlığı olarak görünmesini istedim. Bunun için MessagePanel’in yapılandırıcısına hideTitle parametresi için TRUE değeri gönderdim, ModalWindow’dan miras aldığım setTitle() ile ModalWindow’a başlık ataması yaptım.

ModalWindow’u göstermek için show() yordamını kullanıyoruz. Bunun için Ajax desteği olan bir bileşenin oluşturduğu istek ile AjaxRequestTarget nesnesi gelmesi gerekmektedir.

MessageModal’ın bir de HTML dosyası olmalı :

<wicket:panel>
    <div wicket:id="content"/>
</wicket:panel>


Modal kullanımları için ilgili modal kodlarını içerecek bir Wicket Bileşeni tanımlamamız gerekmektedir.

<div wicket:id="modal"></div>

MessagePanel oluşturur gibi MessageModal oluşturabiliriz:

Message message = new Message(MessageType.SUCCESS,
        "message.modal.title",
        "message.modal.body",
        "message.modal.footer");
message.addButton(new MessageButton("message.button.no"){
    @Override
    public void clickButton(AjaxRequestTarget target) {
        // DO NOTHING
    }
});
message.addButton(new MessageButton("message.button.yes"){
    @Override
    public void clickButton(AjaxRequestTarget target) {
        // DO NOTHING
    }
});
MessageModal messageModal = new MessageModal("modal", message);

Göstermek için ise bir Ajax olayına ihtiyacımız var. Bunun için bir önceki bölümde oluşturduğumuz MessagePanel nesnesine “Show” isimli yeni bir buton ekliyorum.

message.addButton(new MessageButton("message.button.show"){
    @Override
    public void clickButton(AjaxRequestTarget target) {
        modal.showMessage(target);
    }
});

Uygulamayı çalıştırıp sonucunu görelim:




Proje kodlarına buradan ulaşabilirsiniz.

Taner Diler
taner.diler[et]gmail.com

Yorum

Çok beğendim. Giriş - gelişme - sonuç harika olmuş.
Çok güzel ve faydalı bir yazı olmuş, teşekkür ederim. Sırada wicket var gibi :)



ya da
CAPTCHA Images