Showing posts with label ADF. Show all posts
Showing posts with label ADF. Show all posts

Tuesday, December 15, 2015

ADF LOV with Sub-Query parameter

Pada ADF biasanya sebuah LOV (List Of Value) berasal dari sebuah VO (View Object).
Apabila sebuah LOV memiliki dependensi parameter dari field lainnya pada UI, maka biasanya query pada VO nya akan memiliki :param.

Misalnya seperti berikut:
select distinct xx.kolom_1, xx.kolom_2
from table_xx xx, (
    select kolom_a, kolom_b
    from table
    where kolom_c = :param1
) yy
where 
xx.kolom_3 = yy.kolom_a
yy.kolom_b = :param2 

pada umumnya untuk menghandle masalah depedensi ini kita akan menggunakan view criteria atau bindParameter.

Cara kerja dari view criteria adalah membungkus query vo dan menambahkan where clause menjadi seperti berikut:

select * from (
    select distinct xx.kolom_1, xx.kolom_2
    from table_xx xx, (
        select kolom_a, kolom_b
        from table
        where kolom_c = :param1
    ) yy
    where 
    xx.kolom_3 = yy.kolom_a
    yy.kolom_b = :param2
    )
where ....
 
dan sebuah viewcriteria membutuhkan query yang memiliki kolom yang akan menjadi filter.
jadi untuk query yang menggunakan distinct akan bermasalah, karena mungkin saja kolom yang akan menjadi filter tersebut mempengaruhi jumah data hasil query

select * from (
    select distinct xx.kolom_1, xx.kolom_2, yy.kolom_b
    from table_xx xx, (
        select kolom_a, kolom_b
        from table
        where kolom_c = :param1
    ) yy
    where 
    xx.kolom_3 = yy.kolom_a
    yy.kolom_b = :param2
    )
where kolom_b = :parameter

Tidak hanya untuk query DISTINCT saja, karena ada beberapa sql lainnya yang tidak bisa dibungkus oleh where clause contohnya seperti START WITH dan CONNECT BY 

Sedangkan bind parameter untuk parameter pada where clause itu sejauh yang saya coba tidak bisa di implement pada sebuah LOV dengan VO yang memiliki query seperti diatas.

Lalu apa bagaimana caranya?
VO di ADF kan hanya bisa menggunakan viewcriteria dan bind parameter untuk mengisi parameter pada query?

Setelah bertukar pikiran dalam diskusi masalah ini, akhirnya di temukan sebuah trik untuk menyelesaikan masalah ini.
Yaitu dengan mengganti query dari VO tersebut secara programmatic pada saat pop-up dialog LOV muncul, atau dengan kata lain adalah meng-harcode parameter kedalam query tersebut.

berikut gambarannya :

select distinct xx.kolom_1, xx.kolom_2
from table_xx xx, (
    select kolom_a, kolom_b
    from table
    where kolom_c = 'ABCDEF'
) yy
where 
xx.kolom_3 = yy.kolom_a
yy.kolom_b = 'GHIJK' 


Oke diatas adalah overview dari apa yang akan di jelaskan secara detail selanjutnya.

Pertama-tama akan saya berikan contoh untuk mengganti query dari sebuah VO.
Pada contoh berikut query VO diganti didalam sebuah method di dalam ViewObjectImpl

public class VoTableSearchImpl {
  /**
   * This is the default constructor (do not remove).
   */
  public VoTableSearchImpl() {
  }

  public void execSearchQuery(String param1, String param2) {
   
    setFullSqlMode(FULLSQL_MODE_AUGMENTATION); 
    setQuery("select distinct xx.kolom_1, xx.kolom_2
            from table_xx xx, (
                select kolom_a, kolom_b
                from table
                where kolom_c = '"+ param1 +"'
            ) yy
            where 
            xx.kolom_3 = yy.kolom_a
            yy.kolom_b = "+ param2 +"'");
    executeQuery();
  }

}

yang perlu diperhatikan adalah semua nama kolom, jumlah kolom, dan type data nya harus sama antara query awal dengan query yang baru


selanjutnya adalah mengimplementasikannya kedalam sebuah LOV

akan ditaruh dalam sebuah method listener pada LOV yaitu launchPopupListener
dan ada satu method tambahan yang diperlukan untuk bisa mendapatkan VO dari suatu LOV

private ViewObject getLovVo(String attributeBinding) {
        BindingContext bctx = BindingContext.getCurrent();
        BindingContainer bindings = bctx.getCurrentBindingsEntry();
        FacesCtrlLOVBinding lov = (FacesCtrlLOVBinding)bindings.get(attributeBinding);
        return lov.getListIterBinding().getViewObject();
    }

dan berikut adalah isi dari method launchPopupListener

public void onLaunchLovNama(LaunchPopupEvent launchPopupEvent) {
    String param1 = (String)getAttrBinding("param1").getAttributeValue();
    String param2 = (String)getAttrBinding("param2").getAttributeValue();
   
    VoTableSearchImpl vo = (VoTableSearchImpl)getLovVo("lovNama");           
    vo.setFullSqlMode(VoTableSearchImpl.FULLSQL_MODE_AUGMENTATION);
    vo.setQuery("select distinct xx.kolom_1, xx.kolom_2
            from table_xx xx, (
                select kolom_a, kolom_b
                from table
                where kolom_c = '"+ param1 +"'
            ) yy
            where 
            xx.kolom_3 = yy.kolom_a
            yy.kolom_b = "+ param2 +"'");
    vo.executeQuery();
}

Sekian dulu dan selamat mencoba.

ADF fileDownloadActionListener custom validation (ADF Auto Click Button)

Pada adf ada komponen <af:fileDownloadActionListener> yang biasanya digunakan untuk menggenerate mendownload suatu file.
<af:fileDownloadActionListener> biasanya di pasang pada sebuah button.

fileDownloadActionListener bisa dibilang memiliki kelemahan, yaitu saat button di klik maka fileDownloadActionListener akan langsung ter-trigger, tanpa peduli dengan validasi pada form input yang ada, padahal ada field-field mandatory yang harus diisi dengan value yang benar (misalnya sesuai dengan data yang ada pada database). Tetapi asalkan field tersebut tidak di isi kosong maka fileDownloadActionListener akan langsung merespon, padahal field tersebut di isi dengan data yang tidak ada di database.

Dan sepertinya dari sananya (oracle) memang tidak membuat <af:fileDownloadActionListener> ini bisa di intercept begitu ter-trigger.
Jadi untuk mencegah hal ini terjadi kita akan menggunakan sebuah trick, yaitu dengan menggunakan hidden button dan script auto click.

Button yang dipasang <af:fileDownloadActionListener> akan kita hidden (kita sebut saja button H), dan menambahkan button lain (kita sebut saja button D).
Pada saat button D di klik maka akan tervalidasi field-field mandatory yang ada pada form, jika validasi lolos selanjutnya akan di running script untuk mengklik button hidden yang dipasang <af:fileDownloadActionListener>
File akan digenerate dan proses download bisa dilakukan


Berikut penjelasan lebih lanjutnya:

  • Ini adalah gambar structure komponen dari Button dan fileDownloadListener nya


  • Code untuk Button Hidden
<af:commandButton text="Button Hidden"
    clientComponent="true" visible="false"
    binding="#{backingBeanScope.backing_view1.cb2}"
    id="cb2" partialSubmit="false">
    <af:fileDownloadActionListener filename="asdasd"
           method="#{backingBeanScope.backing_view1.downloadMe}"
           contentType="text/plain; charset=utf-8"/>
</af:commandButton>

  • Code untuk Button Download
<af:commandButton text="Button Download" id="cb1"
    binding="#{backingBeanScope.backing_view1.cb1}"
    action="#{backingBeanScope.backing_view1.prepareForDownloadAction}"
    clientComponent="true"
    partialSubmit="true"/>

  • Code javascript untuk file jspx. Ini adalah script Auto Click nya, nanti akan di panggil melalui java code
<af:resource type="javascript">
            function customHandler(event) {
                var exportCmd = AdfPage.PAGE.findComponentByAbsoluteId("cb2");
                var actionEvent = new AdfActionEvent(exportCmd);
                actionEvent.forceFullSubmit();
                actionEvent.noResponseExpected();
                actionEvent.queue();
            }
</af:resource> 

  • dan berikut adalah method action pada Button Download
public String prepareForDownloadAction() {

    // ---- logic validasi ---- //

    // ---- memanggil function javascript ---- //
    FacesContext context = FacesContext.getCurrentInstance();
    ExtendedRenderKitService erks = Service.getService(context.getRenderKit(), 
                                        ExtendedRenderKitService.class);
    erks.addScript(context, "customHandler();");
    return null;
}


Cukup sekian dan silahkan dicoba.












Thursday, December 18, 2014

ADF - Shuttle Component

Pada kesempatan ini, saya akan membahas tentang Shuttle Component. Shuttle Component sendiri merupakan sebuah komponen untuk memilih lebih dari satu buah pilihan. Pada kesempatan ini, saya ingin mempraktekan cara menginsert value dari shuttle component namun value nya di pisahkan dengan tanda ";" . Untuk mendowload project bisa klik disini. Sebelum nya, saya akan menjelaskan sedikit arsitektur project yang akan saya buat. 

Di dalam project ini, saya menggunakan 2 buah table, table pertama menggunakan table department yang bisa di ambil dari database HR serta 1 lagi menggunakan table yang saya buat sendiri untuk menampung hasil insert dari shuttle. Table Department akan digunakan untuk dijadikan sebagain LOV untuk shuttle component, sedangkan table satu nya untuk menampung hasil insert data dan di tampilkan ke dalam table.

  • Berikut tampilan main.jspx yang terdiri dari table yang saya buat dan 1 buah button untuk membuka popup.

Tampilan main.jspx
  • Berikut tampilan popup untuk insert data, yang terdiri dari 1 buah shuttle component, 1 input text dan 1 buah button. Pada screen ini, kita diminta untuk memasukkan nama, serta nama department yang dimana department bisa dipilih lebih dari 1, jika sudah button insert berfungsi untuk memasukkan data ke dalam table yang sudah kita buat di main screen.

Tampilan Insert Data Pada Pop Up
  • Kita masukkan ActionListener pada button yang ada di dalam popup screen. Jika belum pernah membuat method nya bisa di buat.
ActionListener Pada Button Add
  • Untuk memasukkan Shuttle Component, kita bisa mendrag data control yang ingin kita buat shuttle ke dalam jspx, sebagai contoh saya akan membuat shuttle dari DepartmentView1. Kemudian pilih Multiple Selection dan pilih ADF Select Many Shuttle. Jika sudah, kita pilih Base Attribute nya menjadi DepartmentName & DisplayAttribute nya menjadi DepartmentName.
Cara Add Shuttle Component

Pilihan Ketika Add Shuttle Component

  • Berikut Sample Java Code nya.
 package com.demo.blog.bean;  
 import com.demo.blog.model.TableCreationVoImpl;  
 import com.demo.blog.model.TableCreationVoRowImpl;  
 import com.demo.blog.model.common.AppModule;  
 import javax.el.ELContext;  
 import javax.el.ExpressionFactory;  
 import javax.el.ValueExpression;  
 import javax.faces.application.Application;  
 import javax.faces.context.FacesContext;  
 import javax.faces.event.ActionEvent;  
 import oracle.adf.model.BindingContext;  
 import oracle.adf.model.binding.DCBindingContainer;  
 import oracle.adf.model.binding.DCDataControl;  
 import oracle.adf.model.binding.DCIteratorBinding;  
 import oracle.adf.view.rich.component.rich.input.RichInputText;  
 import oracle.binding.BindingContainer;  
 import oracle.jbo.Row;  
 import oracle.jbo.ViewObject;  
 import oracle.jbo.uicli.binding.JUCtrlListBinding;  
 public class Bean {  
   private final String TABLECREATION_ITERATOR = "TableCreationVo1Iterator";  
   private RichInputText nama;  
   public Bean() {  
   }  
   public BindingContainer getBindings() {  
     return oracle.adf.model.BindingContext.getCurrent().getCurrentBindingsEntry();  
   }  
   public void SaveAndClose(ActionEvent actionEvent) {  
     // Add event code here...  
     oracle.binding.BindingContainer bc = this.getBindings();  
     JUCtrlListBinding listBinding =  
       (JUCtrlListBinding)bc.get("DepartmentView1");  
     BindingContext bindingContext = BindingContext.getCurrent();  
     DCDataControl dc =  
       bindingContext.findDataControl("AppModuleDataControl");  
     AppModule appM = (AppModule)dc.getDataProvider();  
     String departmentVar = "";  
     Object[] str = listBinding.getSelectedValues();  
     if (str != null && str.length > 0) {  
       for (int i = 0; i < str.length; i++) {  
         if (i == 0) {  
           departmentVar = str[i].toString();  
         } else {  
           departmentVar = departmentVar + " ; " + str[i];  
         }  
       }  
       listBinding.clearSelectedIndices();  
     }  
     String name = getNama().getValue().toString();  
     AppModule appModule = getAppModuleDataControl();  
     TableCreationVoImpl vo =  
       (TableCreationVoImpl)this.getViewObjectByIterator(TABLECREATION_ITERATOR);  
     Row newRecord = vo.createRow();  
     newRecord.setAttribute(TableCreationVoRowImpl.DEPARTMENT,  
                 departmentVar);  
     newRecord.setAttribute(TableCreationVoRowImpl.NAME, name);  
     vo.insertRow(newRecord);  
   }  
   private ViewObject getViewObjectByIterator(String iteratorName) {  
     DCBindingContainer bindings2 =  
       (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();  
     DCIteratorBinding dcItteratorBindings =  
       bindings2.findIteratorBinding(iteratorName);  
     // Get an object representing the table and what may be selected within it  
     ViewObject vo = dcItteratorBindings.getViewObject();  
     return vo;  
   }  
   public static AppModule getAppModuleDataControl() {  
     FacesContext fc = FacesContext.getCurrentInstance();  
     Application app = fc.getApplication();  
     ExpressionFactory elFactory = app.getExpressionFactory();  
     ELContext elContext = fc.getELContext();  
     ValueExpression valueExp =  
       elFactory.createValueExpression(elContext, "#{data.AppModuleDataControl.dataProvider}",  
                       AppModule.class);  
     return (AppModule)valueExp.getValue(elContext);  
   }  
   public void setNama(RichInputText nama) {  
     this.nama = nama;  
   }  
   public RichInputText getNama() {  
     return nama;  
   }  
 }  
  • Berikut hasil nya jika main.jspx nya di run.
Tampilan Ketika Page Dijalankan Pertama Kali
Tampilan Ketika Button Insert Data Di Klik
Cara Isi Insert Data yang dilanjutkan dengan mengklik Add
Tampilan Table Setelah Data Di Add
Contoh Add data sebanyak yang di pilih
Tampilan Akhir Page
Semoga bermanfaat !
Terima Kasih.



ADF - Dynamic Region

Pada kesempatan ini saya ingin berbagi tentang ADF Dynamic Region. Apa sih Dynamic Region itu? Dynamic Region itu adalah sebuah region yang bisa berubah secara dinamis dari region 1 ke region lain nya berdasarkan kondisi tertentu. Sebagai contoh, saya akan membuat sebuah dynamic region yang terdiri dari 2 buah region yang bisa di switch dari region 1 ke region 2. Oke sekarang kita langsung masuk ke step by step nya.
  • Buat aplikasi ADF, jika sudah memberi nama bisa langsung di klik Finish. Expand folder View Controller, kemudian klik kanan di folder pageflow lalu pilih new. Buat ADF Task Flow, kemudian beri nama TaskFlowSatu. Buat 2 taskFlow lagi dan beri nama TaskFlowDua dan TaskFlowMain. TaskFlowSatu dan TaskFlowDua yang akan digabungkan kedalam TaskFlowMain.

  • Pada masing masing taskFlow buat sebuah View, drag and drop view tersebut ke dalam taskflow yang sudah dibuat, lalu double klik, kemudian beri nama view sesuai taskFlow nya, sebagai contoh saya akan memberi nama ViewTaskFlowSatu, ViewTaskFlowDua, ViewTaskFlowMain.

  • Buka ViewTaskFlowSatu yang tadi sudah kita buat, kemudian design seperti contoh gambar dibawah

  • Buka ViewTaskFlowDua yang tadi sudah kita buat, kemudian design seperti contoh gambar dibawah.

  • Buka ViewTaskFlowMain, kemudian tarik TaskFlowSatu ke dalam ViewTaskFlowMain, lalu akan muncul pilihan akan kita jadikan sebagai region atau dynamic region, pilih Dynamic Region.

  • Jika sebelum nya kita belum membuat managedbean nya, klik tanda tambah, kemudian isi sesuai gambar dibawah.

  • Buka TaskFlowMain, kemudian pilih overview. Klik pada bagian Managed Bean, pada bagian scope, ganti dari backingbean menjadi view.

  • Buka kembali ViewTaskFlowMain, kemudian klik binding pada bagian bawah, lalu edit taskFlow binding yang awalnya mengambil value dari backingBean menjadi viewScope.

  • Jika Sudah, tambahkan 2 buah button di dalam ViewTaskFlowMain. Kemudian cari component SetPropertyListener dan masukkan kedalam masing masing button yang sudah kita buat tadi. Sebelum nya buat String yg diberi nama currentTF pada java class yang sudah kita buat tadi di awal.


  • Pada bagian struktur file, klik region yang sudah kita buat tadi, kemudian buka property lalu pilih partial trigger kemudian edit. Pindahkan 2 button yang sudah kita buat ke kanan kemudian klik ok. 

  • Buat file JSP pada folder View Controller, kemudian drag and drop TaskFlowMain ke dalam file JSPX yang sudah kita buat.

  • Step terakhir, buka java class yang sudah kita buat tadi kemudian kita isi dengan contoh berikut :
 import oracle.adf.controller.TaskFlowId;  
 public class bean {  
   private String taskFlowId = "/WEB-INF/taskFlow1.xml#taskFlow1";  
   private String currentTF = "taskflow1";  
   private String taskFlow2 = "/WEB-INF/taskFlow2.xml#taskFlow2";  
   public bean() {  
   }  
   public TaskFlowId getDynamicTaskFlowId() {  
     if (this.getCurrentTF().equalsIgnoreCase("taskflow1"))  
     return TaskFlowId.parse(taskFlowId);  
     else   
       return TaskFlowId.parse(taskFlow2);  
   }  
   public void setCurrentTF(String currentTF) {  
     this.currentTF = currentTF;  
   }  
   public String getCurrentTF() {  
     return currentTF;  
   }  
 }  

  • Jika sudah run file jspx yang sudah kita buat tadi. Maka akan muncul seperti gambar dibawah, jika kita klik button taskFlowSatu, maka yang akan muncul tampilan dari taskFlowSatu, namun jika kita klik button TaskFlowDua, maka yang akan muncul tampilan dari TaskFlowDua.


Terima Kasih, semoga bermanfaat !
Untuk mendownload project bisa di klik disini 

Monday, December 15, 2014

Reuse Aplikasi ADF dengan Menggunakan Bounded ADF Taskflow

Salah satu fitur dari ADF adalah Task Flow. Daripada membangun sebuah aplikasi web raksasa dalam sebuah halaman JSF, kita dapat membagi fitur-fitur dalam JSF tersebut menjadi beberapa fragment yang disebut Task Flow. Taskflow dapat di-reuse dan dapat juga dipisah pengerjaannya sehingga memungkinan pengembangan aplikasi yang modular.

Sebagai contoh: Anda diminta untuk membangun aplikasi ADF untuk kebutuhan Human Resource (HR) dan Finance. Aplikasi HR akan menampilkan data karyawan dan jadwal cuti, sedangkan aplikasi Finance akan menampilkan data karyawan dan claim. Kedua aplikasi tersebut sama-sama memerlukan interface yang menampilkan data karyawan. Akan lebih baik apabila data karyawan tersebut dibangun menjadi aplikasi tersendiri yang dapat di-consume oleh module HR dan Finance sehingga menghemat waktu pengerjaan dan mencegah duplikasi.

Langkah-langkah pembuatan dan cara meng-consume task flow saya contohkan dalam guide berikut:

1. Create New Project ADF. Kemudian pada ViewController anda, klik kanan -> New -> ADF Task Flow


 2. Beri nama task flow anda (sesuaikan dengan functionality task flow tersebut). Pilih Create as Bounded Task Flow dan Create with Page Fragment (2 opsi ini akan saya jelaskan pada blog lain).


3. Setelah task flow anda terbentuk, masukkan 2 komponen View dalam task flow tersebut. Selain itu, tambahkan juga 2 komponen Control Flow Case sehingga hasil akhir task flow adalah seperti di bawah ini:



 4. Untuk masing-masing view, double click kemudian buat jsff yang sesuai untuk setiap view. Dalam jsff tersebut, saya membuat output text dan button untuk masing-masing page sehingga hasil akhirnya kira-kira sebagai berikut:


 5. Untuk masing-masing button, buka Component Properties. Dalam button action, tambahkan go/back sesuai dengan Control Flow Case pada Step no 3.


6. Setelah Task Flow anda selesai, klik kanan pada Project View Controller Anda. Buka Project Properties.


7. Buka tab Deployment -> New -> ADF Library JAR file. Ini akan membuat deployment profile baru yang memungkinkan anda untuk men-deploy aplikasi menjadi Library ADF yang dapat di-reuse di project lain.


8. Hasil Deployment profile baru adalah sbb:


9. Setelah selesai, klik kanan ViewController Project anda, lalu deploy sebagai jar sesuai dengan deployment profile yang telah dibuat pada step nomor 8


 10. Next dan Finish untuk step berikutnya.


11. Anda dapat melihat lokasi dimana file .jar anda terbentuk. Dalam contoh saya, file tersebut ada pada directory: /Users/darwincuputra/jdeveloper/mywork/ADFTaskFlow/ADFTaskFlowViewController/deploy. Anda dapat mengakses file tersebut dari directory ini.


12. Buat aplikasi baru, dimana aplikasi ini merupakan aplikasi yang akan meng-consume/ me-reuse Task Flow yang sudah anda buat pada step nomor 1-11. Di Resource Palette, buat New Connection -> File System


13. Masukkan Connection Name, lalu pada Directory Path, masukkan directory dimana file .jar yang berisi task flow berada.


14. Setelah selesai, anda dapat meng-expand directory tersebut untuk melihat task flow apa saja yang ada dalam sebuah library.


15. Pilih task flow yang anda inginkan, Drag and Drop pada page yang anda buat. Pilih Region


16. Add Library untuk meng-include semua library yang dibutuhkan untuk menjalankan Task Flow yang anda include.


17. Hasilnya adalah sbb:


18. Klik kanan -> Run -> buka pada browser anda. Page akan muncul beserta task flow yang anda include.


Selamat, Anda berhasil membuat task flow dan me-reuse task flow tersebut dalam aplikasi lain.
Pada blog berikutnya akan saya jelaskan secara lebih mendetail tentang task flow (bounded, unbounded, region, dsb). Semoga membantu!