Wednesday, May 20, 2020

Drag and Drop Shuttle Box in Oracle Apex Application




In diesem Blog möchten wir zeigen wie man eine Shuttle Box mit ein wenig JavaScript und CSS zeitgemäßer wirken lassen kann. Hierfür verwenden wir unsere Drag and Drop Funktion, die wir in einem älteren Blog erstellt hatten und modifizieren diese noch um ein paar weitere Features. So wollen wir zum Beispiel ausgewählte Werte auch wieder entfernen und sortieren können. Die Ergebnisse speichern wir in einer APEX Collection, so dass wir diese in unsere Anwendung weiterverwenden können.

Zunächst benötigen wir für unser Beispiel eine Tabelle mit Werten, die wir selektieren möchten. Hierzu können wir die Beispieldatenbank "EMP/DEPT" verwenden, die ganz einfach im APEX Builder unter "SQL Workshop > Utilities > Sample Datasets" installiert werden kann.




Anschließend können wir in APEX eine neue Anwendung mit einer leeren Seite erstellen in der wir die Drag and Drop Funktion implementieren.

Für unsere Drag and Drop Funktion benötigen wir als erstes zwei „Custom Templates“. Diese werden unter „Shared Components > Templates“ erstellt. Als Template Type wählen wir „Report“ und erstellen dieses „From Scratch“. Unser erstes Template nennen wir beispielhaft „DragZone“. Wählen dann die Template Class „Custom 1“ und den Template Type „Named Column (row template)“ und klicken „Create“ um das Custom Template zu erstellen.




Anschließend erstellen wir auf gleicher Weise ein zweites Template welches wir „DropZone“ nennen.

Als nächstes müssen wir die Struktur von unserem Report definieren. Hierzu klicken wir als erstes auf das Template „DragZone“ und ersetzen den HTML Code für das „Row Template 1“ durch folgenden HTML Code.

<div>
    <li data-empno='#EMPNO#' data-ename='#ENAME#' class='draglist-el' draggable=true>#ENAME#<hr></li>
</div>

Da wir eine ungeordnete Liste benötigen, müssen wir noch zuvor ein HTML Open Tag hinzufügen bevor alle Listelemente angezeigt werden können und abschließend einen Close Tag. Hierzu in der Region „Before Rows“ folgende HTML Code einfügen.

<ul class='draglist' ondragstart='handleDrag(event)'>

 Und in der Region „After Rows“ den Close Tag.

</ul>

Final mit „Apply Changes“ die Änderungen speichern.


Als nächstes definieren wir unsere „DropZone“ und klicken dazu auf das Template „DropZone“ und ersetzen den HTML Code für das „Row Template 1“ durch folgenden HTML Code.

<div>
    <li data-empno='#EMPNO#' data-ename='#ENAME#' class='droplist-el' >
        <button type="button" data-empno='#EMPNO#' data-seq_id='#SEQ_ID#' class="del" style="padding-left:0px;border:none;background:none" ><span class="fa fa-trash" style="color:#0076df;"></span></button>
        <button type="button" data-seq_id='#SEQ_ID#' class="down" style="padding-left:0px;border:none;background:none" ><span class="fa fa-arrow-up" style="color:#0076df;"></span></button>
        <button type="button" data-seq_id='#SEQ_ID#' class="up"   style="padding-left:0px;border:none;background:none" ><span class="fa fa-arrow-down" style="color:#0076df;"></span></button>
        #ENAME#<hr></li>
</div>

An dieser Stelle haben wir zusätzlich einen Button zum entfernen, sowie jeweils einen Button zum Auf und Ab sortieren hinzugefügt.

Da wir wieder eine ungeordnete Liste benötigen, müssen wir noch zuvor ein HTML Open Tag hinzufügen bevor alle Listelemente angezeigt werden können und abschließend einen Close Tag. Hierzu in der Region „Before Rows“ folgende HTML Code einfügen.

<ul class='droplist' ondragstart='handleDrag(event)'>

 Und in der Region „After Rows“ den Close Tag.

</ul>

Final mit „Apply Changes“ die Änderungen speichern.


Jetzt sind unsere benötigten Templates fertig, so dass wir wieder zurück zum Page Editor gehen können. Dort benötigen wir als nächsten Schritt einen Classic Report für die DragZone. Als Source verwenden wir die erstellte Tabelle „EMP“ und sortieren diese nach „ENAME“.




Dann noch unter „Attributes“ das Custom Template „DragZone“ auswählen.





Als nächstes benötigen wir einen weiteren Classic Report für unsere „DropZone“. Da wir die ausgewählten Werte in einer „APEX Collection“ speichern, verwenden wir unter Source folgendes SQL Query.

select seq_id, c002 as ename from apex_collections where collection_name = 'DROPZONE'
order by seq_id

Dann noch unter „Attributes“ das Custom Template „DropZone“ auswählen.

Jetzt noch den Classic Report´s eine Static-Id hinzufügen („dragzone“ & „dropzone“) sowie bei beiden Reports folgende Einträge unter Custom Attributes einfügen:

ondragover='handleDragover(event)'   ondrop='handleDrop(event)'

Optional kann noch ein wenig CSS für die beiden Reports hingezufügt werden. Dazu im Page-Inline-CSS-Editor folgende Code einfügen.

.draglist{
      list-style-typenone;
}

.droplist{
      list-style-typedecimal-leading-zero;
}

.draglist-el {
      padding-top5px;
        padding-bottom5px;
}

.droplist-el {
      padding-top5px;
        padding-bottom5px;
}

hr {  
  margin-top5px
  margin-bottom0px
}


Für unsere Drag & Drop Funktion benötigen wir nun noch JavaScript Function´s, die wir im Code Editor für die „Function and Global Variable Declaration“ hinterlegen.

function handleDrag(event) {
    var data = {empno: event.target.dataset.empnoename: event.target.dataset.ename};
    var json_data = JSON.stringify(data);
    event.dataTransfer.setData('data'json_data);
}

function handleDragover(event){
    event.preventDefault();
}

function handleDrop(event){
    var empno = JSON.parse(event.dataTransfer.getData('data')).empno;
    var ename = JSON.parse(event.dataTransfer.getData('data')).ename;

    apex.server.process(
          'Update Collection',
          {
             x01: empno,
             x02: ename 
          },
          {
             success: function(){
                   apex.event.trigger('#dropzone''apexrefresh')
             },
             dataType: 'text'
          });
}

Da wir hier bei der „handleDrop“ Function einen APEX Process ausführen wollen, der die APEX Collection updaten soll, benötigen wir jetzt noch diesen Process.

Hierzu unter „Proccessing“ einen neuen „Ajax Callback“ Process hinzufügen den wir „Update Collection“ nennen. Im PL/SQL Editor folgenden Code einfügen:

apex_collection.add_member(p_collection_name => 'DROPZONE'
                          ,p_c001 => apex_application.g_x01 
                          ,p_c002 => apex_application.g_x02
                          );

Da unsere Anwendung die APEX Collection jedoch noch nicht erstellt hat benötigen wir noch einen „Page Load Process“ den wir „Create Collection“ nennen. Im PL/SQL Editor folgenden Code einfügen:

if APEX_COLLECTION.COLLECTION_EXISTS(p_collection_name => 'DROPZONE'then
    APEX_COLLECTION.TRUNCATE_COLLECTION(p_collection_name => 'DROPZONE');    
else    
    apex_collection.create_collection('DROPZONE');
end if

Zuletzt benötigen wir noch die Processes zum löschen und sortieren der DropZone Werte.

Hierzu erstellen wir 3 weitere „Ajax Callback Processes“. 
  • Delete Item
  • MoveUp Item
  • MoveDown Item
Zusätzlich benötigen wir noch ein Page Item indem wir die selektierte Id speichern. Dieses nennen wir „P1_ITEM“ und fügen es dem Classic Report „DropZone“ hinzu.

Für den Process „Delete Item“ geben wir folgenden PL/SQL Code ein:

APEX_COLLECTION.DELETE_MEMBER(
    p_collection_name => 'DROPZONE',
    p_seq => :P1_ITEM);
        
APEX_COLLECTION.RESEQUENCE_COLLECTION (
p_collection_name => 'DROPZONE'); 

Für den Process „MoveUp“ Item geben wir folgenden PL/SQL Code ein:

APEX_COLLECTION.MOVE_MEMBER_UP(
    p_collection_name => 'DROPZONE',
    p_seq => :P1_ITEM);

Für den Process „MoveDown“ Item geben wir folgenden PL/SQL Code ein:

APEX_COLLECTION.MOVE_MEMBER_DOWN(
    p_collection_name => 'DROPZONE',
    p_seq => :P1_ITEM);   

Jetzt noch 3 Dynamic Actions die den jeweiligen Process beim Klick auf das jeweilige Icon auslösen. 
  • Delete Item
  • MoveUp Item
  • MoveDown Item

Alle 3 DA´s müssen als Event „Click“ sowie den Selection Type „jQuery Selector“ hinterlegt bekommen. Zusätzlich noch den Event Scope auf „Dynamic“ setzen.

Bei der DA „Delete Item“ muss der jQuery Selector „.del“ eingegeben werden.
Bei „MoveUp Item“ muss der jQuery Selector „.up“ eingegeben werden.
Und bei „MoveDown Item“ muss der jQuery Selector „.down“ eingegeben werden.

Als „True“ Actions benötigen wir für jede Dynamic Actions folgende 3 Events.

1) Execute Javascript Code

$s('P1_ITEM'$(this.triggeringElement).data('seq_id'));

2) Execute PL/SQL Code
  • PL/SQL Code: null;
  • Items to Submit: P1_ITEM 
3) Execute Javascript Code (Delete Dropzone Item)

apex.server.process(
    'Delete Item',
    {
      x01: null
    },
    {
       success: function(){
             apex.event.trigger('#dropzone', 'apexrefresh')
       },
       dataType: 'text'
    });


     Execute Javascript Code (MoveUp Dropzone Item)

apex.server.process(
    'MoveUp Item',
    {
      x01: null
    },
    {
       success: function(){
             apex.event.trigger('#dropzone', 'apexrefresh')
       },
       dataType: 'text'
    });


     Execute Javascript Code (MoveDown Dropzone Item)

apex.server.process(
    'MoveDown Item',
    {
      x01: null
    },
    {
       success: function(){
             apex.event.trigger('#dropzone', 'apexrefresh')
       },
       dataType: 'text'
    });



Alle Änderungen speichern und die Anwendung ausführen :-)






Optionaler Tipp für Tabletts:

Um die Touchfähigkeit auf Tabletts zu gewährleisten muss noch folgende jQuery Datei zu den Static Files hochgeladen werden und auf der Seite wo die Drop Down Region ist hinterlegt werden (JavaScript File URLs).

https://github.com/furf/jquery-ui-touch-punch/blob/master/jquery.ui.touch-punch.js

jQuery UI Touch Punch verwendet simulierte Ereignisse, um Berührungsereignisse analog Mausereignisse zuzuordnen. 


Quellen:

Labels: , ,