14 Mayıs 2013 Salı

JSF Ajax Requestlerdeki Tarayıcı Geri Butonu Sorunu ve Dojo İle Çözümü

Yine JSF kodlama ve yeni bir sorun ile karşı karşıyayız :) Aslında problem tam olarak JSF ile ilgili değil. Bir web uygulaması içerisinde AJAX request gönderildiğinde, URL üzerinde herhangi bir değişiklik olmuyor ise, tarayıcılar, hash değeri değişmediği için, içinde bulunduğu sayfayı tarayıcının History stack'ine eklemiyor. Dolayısıyla siz aynı sayfanın üzerinde bir çok işlem yapsanız bile, tarayıcıdaki sayfa adresi değişmediği için geri ve ileri butonlarını kullanamıyoruz.

Bu probleme çözümler mevcut aslında. Çoğu kişi tarayıcı geri-ileri butonlarını kullanıcıya kullandırtmak yerine uygulama içerisinde kendisinin oluşturduğu geri-ileri butonlarını javascript metodları vasıtasıyla kullandırtıyor. Fakat yine de tarayıcının butonlarına bir çözüm bulunmalı ve mevcut sürece entegre edilmeli. Bu noktada, araştırıp, biraz modifiye ederek uygulamamda kullandığım bir yöntemden bahsedeceğim.

Primefaces 3.5 ve JSF 2.1 kullanarak geliştirdiğim bir uygulamada, Primefaces Datatable elemanı içerisinde yüklü bir listem var ve bir file system dizini gibi, her bir satıra tıklandığında, o satırdaki klasörün içerisine girilmesi gerekiyor. Geri ve ileri butonlarının da çalışması gerekiyor.

Bu noktada çözüm olarak dojo'nun back sınıfını kullandım. Bu kütüphane, her bir click eventi esnasında, history stack'ine bir veri kaydetmemizi sağlıyor. Bu veriyi de, geri ve ileri butonlarına tıklandığı zaman yapacağı işlemi tanımlayarak kaydediyoruz. Böylece kullanıcı geri butonuna bastığı zaman, kaydettiği parametrelerle işlem yapabiliyor.

Kodlarla özetlemek gerekirse:

myfiles.xhtml
<script>
     function HistoryState(fileid)
     {
           this.fileid = fileid;
           this.back = function() { 
                browserBck([{name: 'fileid', value: fileid}]);
           };
           this.forward = function() { 
               browserFrwrd([{name: 'fileid', value: fileid}]);
           };
           this.changeUrl = false;
       }
</script>

<script type="text/javascript"  src="js/dojo.js" djConfig="preventBackButtonFix: false"></script>
<script type="text/javascript">
     dojo.require("dojo.back");
     dojo.back.init();
     dojo.back.setInitialState(new HistoryState(null));
</script>

...<p:dataTable>... 
        <p:ajax event="rowSelect" listener="#{fileController.list()}"/> 
...</p:dataTable>

   <p:remoteCommand name="browserBck" action="{fileController.listBack()}"/> 

Yukarıdaki örnekte HistoryState adında bir nesne oluşturdum. Bu nesnede, dojo için gerekli olan back, forward ve changeUrl değişkenlerini set ettim. back değişkeni, geri butonuna tıklanınca yapılacakları, forward değişkeni ileri butonu için aynı işlemleri tanımlamamızı sağlıyor. changeUrl ise, her bir state kaydolurken dojo tarafından oluşturulan random rakamın adres satırına yazılıp yazılmamasını kontrol ediyor. HTML5 desteği olmayan tarayıcılar için bu değer true olmalı. Sayfa ilk defa render olurken ise setInitialState'i çalıştırıp işlemi başlatıyoruz. Yine aynı dosyada bulunan p:dataTable elemanının bir satırına tıklanınca server tarafında liste yenilemek için <p:ajax> kullandım:

FileController.java
public void list() {
        RequestContext rc = RequestContext.getCurrentInstance();
        rc.execute("dojo.back.addToHistory(new HistoryState('" + selectedFile.getId() + "'));");
        listByParentID(selectedFile.getId());
}

public void listBack() {
        ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext(); 
        String fileID = ec.getRequestParameterMap().get("fileid");
        listByParentID(fileID);
} 

Yukarıdaki örnekte görüldüğü gibi, her bir listeleme işleminde, client tarafındaki addToHistory metoduna, HistoryState nesnemi ,içerisinde mevcut parent id bulunacak şekilde set ediyorum.

Bu örnek çok daha basitleştirilbilirdi fakat JSF ve server side'da detaylı bir örnek bulunsun istedim.

Süreci Özetlersek:
1 - xhtml dosyasında dojo'yu initialize ettik. History'ye kaydedeceğimiz nesneyi ve back-forwad metodlarımızı belirledik
2 - Bir linke tıklayıp server tarafına bir ajax request geçtik. Server tarafındaki metodda "RequestContext.getCurrentInstance().execute()" ile client tarafındaki dojo.back.addHistory metodunu çağırdık.
3 - Tarayıcının geri butonuna bastık. Bu işlem, 1. maddede belirlediğimiz back metodunu çağırdı (client-side).
4 - <p:remoteCommand> ile, 3. maddede çağrılan metodun, server tarafındaki başka bir metodu çağırmasını sağladık.
5 - Server tarafındaki metod ise istediğimiz listelemeyi yaptı ve geri butonumuz çalışmış oldu.

İleri butonu için de, forward nesnesine gerekli fonksiyonu tanımlayarak aynı işlemleri tekrarlamamız lazım.

Not: Bu şekilde çalışabilmesi için 'js' klasörünün içerisinde;
* dojo.js
* back.js
* resources/iframe_history.html

dosyalarının bulunması gerekiyor.

( jsf, primefaces, ajax, browser back button, forward, dojo, dojo.back, dojo.hash, request, xhtml, p:remoteCommand, p:dataTable )
Kaynak: http://blog.andreaskahler.com/2009/09/managing-browser-history-for-ajax-apps.html

Hiç yorum yok:

Yorum Gönder