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-type: none;
}
.droplist{
list-style-type: decimal-leading-zero;
}
.draglist-el {
padding-top: 5px;
padding-bottom: 5px;
}
.droplist-el {
padding-top: 5px;
padding-bottom: 5px;
}
hr {
margin-top: 5px;
margin-bottom: 0px;
}
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.empno, ename: 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'
});
'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'
});
'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'
});
'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:
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: apex, javascript, style