Benutzer-Werkzeuge

Webseiten-Werkzeuge


prog:apex_select2_ajax

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
prog:apex_select2_ajax [2019/01/09 10:49] – [Modell der Daten aus der Tabelle in ein Objekt einlesen] gpipperrprog:apex_select2_ajax [2019/01/14 12:37] (aktuell) – [Lösungansatz] gpipperr
Zeile 1: Zeile 1:
 +=====Oracle Apex 18.2 - Select2 Java Script Library in einem Classic Report integrieren- Interactive Grid Funktionaliät mit JQuery in einem Classic Report nachimplementieren=====
  
 +
 +<fc #008080>**Aufgabe:**</fc> Für eine Ausschilderung auf Basis der Displays von Philips (https://www.philips.de/p-m-pr/digital-signage-losungen ) ist eine Pflegemaske für die Raumzuordnung mit einem entsprechenden Richtungspfeil in der Raumliste zu implementieren. 
 +
 +In der Datenbank wird dabei hinterlegt welches Display für welche Räume zuständig ist. Je nach Standort des Displays wird dann der entsprechende Richtungspfeil für die Veranstaltung in dem Raum angezeigt.
 +
 +
 +Rand-Parameter:
 +
 +  * Die Auswahlliste für den Richtungspfeil soll auch den Pfeil anzeigen! Das ist die Herausforderung!
 +  * Die bearbeiten Zeilen sollen hervorgehoben dargestellt werden
 +  * Der vorherige Richtungspfeil wird mit angezeigt, um die Änderung leichter nachzuvollziehen
 +  * Das Setzen eine Richtungspfeil ordnet den Raum dem Display zu
 +
 +  * Die Anzahl der Räume ist begrenzt => alle Räume können in einer Tabelle auf der Seite dargestellt werden
 +  * 8 Richtungspfeile können ausgewählt werden ( Basierend auf den Pfeilen um APEX Font ( https://apex.oracle.com/pls/apex/f?p=42:icons ) )
 +  * Die Tabelle kann nach den bereits zugeordneten Räumen gefiltert werden
 +
 +
 +So sieht die Maske nun am Ende aus:
 +
 +
 +{{ :prog:apex:apex_show_select2_list_clasic_report.png |  Oracle APEX - dynamsiche Select2 Liste in Clasic Report}}
 +
 +
 +
 +----
 +
 +
 +===Lösungansatz===
 +
 +**1. Versuch:**
 +
 +
 +Im ersten Schritt habe ich das über einen Interactive Grid versucht zu lösen, die Anzahl der Zeilen ergibt sich aus den Räumen, pro Zeile kann über eine Checkbox der Raum zu Display zugeordnet und ein Richtungspfeil ausgewählt werden. D.h. die treibende Query des Berichts ist ein Select über die verfügbaren Räume mit einem Out Join über die bestehenden Zuordnungen. Im der Matrix kann dann der Richtungspfeil gewählt werden beim Click auf die jeweile Zeile. Beim Speichern wird die Default Funktion abgefangen und dann mit einem eigenen Handler das ganze gespeichert ( siehe z.B. hier => [[prog:oracle_apex_interactive_gird|Oracle Apex 5 - Interactive Grid anpassen und konfigurieren - DML ändern bei ORA-22816: unsupported feature with RETURNING clause]].
 +
 +Leider funktioniert das aber dann nicht so recht wie gewünscht, nur die geänderten Daten werden ja beim Submit übertragen und die Oberfläche passt nicht so wirklich zu der an sich einfachen Anforderung einer einfachen Raum Matrix. So ist es z.B. nicht so einfach eigene GUI Elemente in den Bearbeitungsmodus einer einzelnen Tabellenzelle zu implementieren.
 +
 +**2. Versuch**
 +
 +Die Raum Liste mit einem Classic Report darstellen und die Konfiguration der Richtungspfeile über eine dynamisch eingeblendete Select Liste ermöglichen.
 +
 +Als Basis für die Select Liste dient die JavaScript Library select2 => https://select2.org/.
 +
 +Für Oracle Apex gibt es bereits ein Plugin für diese Liste, siehe => https://apex.world/ords/f?p=100:710:4758666274399::::P710_PLG_ID:BE.CTB.SELECT2 von Nick Buytaert.
 +
 +Das Plugin dient in diesem Fall aber mehr dazu die JavaScript Abhängigkeiten in die Seite zu laden und um das Template Objekte für die Auswahl Liste zu erzeugen.
 +
 +
 +<fc #800000>Problem mit der Lösung:</fc>
 +
 +Wird eine Region neu in der Seite geladen (ohne Submit der Seite!) wird der Event Handler (definiert über einen JQuery Selector) nicht mehr an die Html Tabellen Zelle gebunden! 
 +
 +D.h. um die Tabelle neu einzulesen ist immer ein Submit und ein vollständiges Laden der Seite notwendig damit der Eventhandler wieder richtig funktioniert.
 +
 +=> <fc #800000>Mein Fehler</fc> => Einfach den **"Event Scope"** der Dynamic Action auf **"Dynamic"** ( Binds the event handler to the triggering element(s) for the lifetime of the current page, irrespective of any triggering elements being recreated via Partial Page Refresh (PPR)) setzen!
 +
 + 
 +
 +----
 +
 +==== Umsetzung: Dynamisch eine Select2 Auswahl Liste in einem Classic Report verwenden und Auswahl in der DB speichern====
 +
 +
 +=== Voraussetzung===
 +
 +==Select2 Plugin in die Applikation laden==
 +
 +
 +  - Select2 Plugin von https://apex.world/ords/f?p=100:710:4758666274399::::P710_PLG_ID:BE.CTB.SELECT2 herunterladen und auspacken 
 +  - Aus "src\main\plugin" Datei item_type_plugin_be_ctb_select2.sql als Plugin in die Applikation laden
 +
 +
 +
 +==Template Select Liste in der Seite hinterlegen===
 +
 +Diese Select Liste wird nicht auf der Seite direkt dargestellt. 
 +
 +Diese Liste wird mit JQuery geclont ( in meine Beispiel über die Klasse referenziert) und in der jeweiligen Tabellen Zelle dargestellt.
 +
 +Ablauf:
 +
 +  * Page Item Select Liste mit dem Fonts für die Richtungspfeile anlegen ( wie P100_ICON_VALUES) und mit den möglichen Werte versorgen (select oder statische Liste)
 +  * Diese Page Item mit "On Page Load" mit Dynamic Action ("Hide") ausblenden
 +  * CSS Klasse auf dem Element hinterlegen unter Advanced "CSS Clases" "DEFAULT_SELECT_ICON"
 +
 +
 +Nun kann je nach Bedarf auch noch etwas an der Optik mit der Liste über die Klasse gearbeitet werden.
 +
 +
 +----
 +
 +=== Bericht - Classic Report - über die Räume erstellen===
 +
 +
 +Eine klassischen APEX HTML Tabelle auf Basis eines SQL Kommandos mit einem "Classic Report" in die Seite einfügen.
 +
 +
 +Wichtig ist es die ROWNUM in die Abfrage mit aufzunehmen, alternativ kann natürlich auch ein anderer eindeutiger numerischer Schlüssel in der Ergebnismenge verwendet werden.
 +
 +Die ROWNNUM dient später als Schlüssel für eine dedizierte Tabellenzeile und muss mit dieser angezeigt werden.  
 +
 +Wird die Liste in SQL sortiert, die Rownum erst in einem äußeren Select hinzufügen, damit die Rownum auch der eigenen Sortierung folgt.
 +
 +Unter "Attributes" auf dem Classic Report über die Template Options "Alternating Rows" und "Row Highlighting" ausschalten (auf Disable setzen) , wird später manuell durchgeführt.
 +
 +----
 +
 +=== Modell der Daten aus der Tabelle in ein Objekt einlesen ===
 +
 +Wir müssen ja wissen welche Element wie auf der Seite zugeordnet wurden. 
 +
 +Dazu speichern wir uns die Werte in der angezeigten Tabelle in ein Modell ( Ein Recordset in einem Array).
 +
 +Der Java Script Block wird auf Page Ebene in "Function and Global Variable Declaration" hinterlegen.
 +
 +
 +Die Daten werden aus der Tabelle  in ein Model geladen, als Parameter dient eine Spalte der Tabelle, von der Spalte aus wird die ganze Zeile referenziert und ausgewertet, die Spalte ROWNUM dient als Primary Key für das Array.
 +Da ja kein Text für die Icons in der Zelle steht wird die Klasse ausgewertet und der Wert für das Icon ausgelesen.
 +
 +
 +<code javascript>
 +
 +// ---------------- Schritt 1 --------------------
 +// create Model from table view
 +
 +// build the dataset from all rows of the table
 +// build the dataset from all rows of the table
 +function getDataSet( table_rows ,pkRowName) {
 +    var elemName  = {};
 +    var rownum = 0;
 +    var dataset = [];
 +    var data_row = {};
 +    table_rows.each( 
 +          function (idx,elem)  {
 +                  // read one row of the table
 +                  data_row = {};    
 +                  $(elem).closest('tr').children().each( 
 +                        function (idx,elem)  {
 +                            // read the hint which data cell 
 +                            elemName=$(elem).attr("headers");                            
 +                            
 +                            // read and store the value of the cell
 +                            elemValue = elem.innerText;
 +                            
 +                            if (elemValue.length == 0) {
 +                                //console.log("Read length :: " + elemValue.length + 'for row :: '+elemName);
 +                                //console.log($(elem));
 +                                elemValue='-';
 +                                // check if span element exits and read class                                
 +                                $(elem).children().each (
 +                                    function (idx,spanelem)  {
 +                                        elemValue=$(spanelem).attr("class");     
 +                                        elemValue=elemValue.replace(/ fa-2x/g,'');
 +                                        elemValue=elemValue.replace(/fa /g,'');
 +                                        elemValue=elemValue.replace(/ fa/g,'');
 +                                        //console.log("get class  :: " + elemValue + 'for row :: '+$(spanelem));
 +                                    }
 +                               );
 +                            }
 +                            
 +                            data_row[elemName]= elemValue;       
 +                            
 +                            
 +                            // get the primary key (index counter) for the collection
 +                            if (elemName == pkRowName) {
 +                                  rownum=elem.innerText; 
 +                            }                       
 +                                
 +                  );
 +                dataset [rownum]= data_row;              
 +            }
 +     );
 +    return dataset;
 +}
 +
 +
 +// get the orignal value from the dataset for this cell
 +function getCellValueFromDataSet(cell,pkRowName) {
 +    var rownum=-1;
 +     $(cell).closest('tr').children().each( 
 +                        function (idx,elem)  {
 +                            // read the hint which data cell 
 +                            elemName=$(elem).attr("headers");                           
 +                            // get the primary key (index counter) for the collection
 +                            if (elemName == pkRowName) {
 +                                  rownum=elem.innerText; 
 +                            }                       
 +                                
 +                  );
 +    return  dataset[rownum];
 +
 +
 +
 +
 +
 +//-----------------------------------
 +// first init of the dataset
 +var headerColumnName="ICON_DISPLAY";
 +//dataset
 +var dataset = getDataSet($('[headers="'+headerColumnName+'"]'),'ROWNUM');
 +
 +</code>
 +
 +----
 +
 +
 +=== Java Script Funcionen für die Verwendung des Select2 Elementes hinterlegen ===
 +
 +
 +Auf Page Ebene in "Function and Global Variable Declaration" hinterlegen.
 +
 +
 +Init Functions für die Select2 Liste:
 +
 +<code javascript>
 +
 +
 +//-----------------------------------
 +
 +
 +// ------
 +// globals
 +var actCell = {};
 +var lastCell = {};
 +var actCellValue = {};
 +var lastCellValue = {};
 +var actCellText = {};
 +var lastCellText = {};
 +var lastTriggerElement = {};
 +var secondClick=false;
 +
 +
 +
 +// select2 Functions 
 +
 +
 +// ----
 +// get the option list as template
 +function iconSelectList() {
 +   return $('.DEFAULT_SELECT_ICON').clone().addClass('select-icon');
 +}
 +
 +
 +
 +var iconSelectList = iconSelectList();
 +
 +
 +//-------
 +// default text if you open the element
 +var defaultPlaceholderText="Icon auswählen";
 +function getDefaultPlaceholderText (elem){
 +    return defaultFristText;
 +}
 +
 +//--------
 +// set the value of a cell
 +function setCellvalue ( cell, text, textClass) {
 +     var textSpan=$(cell).find('[id^=ROW_]');
 +     console.log('Set Text  => ' + text);    
 +     console.log('Set Class => ' + textClass);
 +    
 +    //only if something was selected
 +    if (text != defaultPlaceholderText) {
 +              // console.debug(cell);              
 +              // $(textSpan).text(text);
 +               $(textSpan).removeClass();
 +               $(textSpan).addClass('fa '+textClass+' fa-2x');
 +             }
 +      else {
 +           // insert the old value
 +         
 +            $(textSpan).text(lastCellText);
 +            $(textSpan). removeClass();        
 +            $(textSpan).addClass('fa ' +lastCellValue +' fa-2x');
 +      }        
 +
 +
 +//--------
 +// get the Value of a cell
 +function getCellValue( cell )  {
 +    var cellValue = {};
 +    var textSpan=$(cell).find('[id^=ROW_]');
 +    cellValue =  $(textSpan).attr('datavalue');
 +    console.log("Read akt value :" + cellValue);
 +    return cellValue;
 +}
 +
 +// get the Text of the cell
 +function getCellText( cell )  {
 +    var cellText = {};
 +    var textSpan=$(cell).find('[id^=ROW_]');
 +    cellText =  $(textSpan).text();
 +    console.log("Read akt text :" + cellText);
 +    return cellText.toString();
 +}
 +
 +
 +//--------------
 +// format select2 Entries
 +function setListEntry(elem) {
 +   var originalOption = elem.element;
 +   return '<i aria-hidden="true" class="fa  '+ elem.id+' fa-2x">     '+elem.text+'</i>'; //iconFont
 +}
 +
 +
 +//---------
 +// return the selected element
 +function returnSelection(elem){ 
 +   return elem.text;
 +}
 +
 +
 +//---------
 +//
 +// Clear the open elementes
 +function clearAll(elem) {
 +    // invalid the last element 
 +    lastTriggerElement = {};
 +    //set the value on the last cell back
 +    if (secondClick) {
 +           
 +      console.log("Auswahl Vaule ->" +  $(".select-icon").val());
 +      console.log("Auswahl text -> " +  $('.select-icon').find(':selected').text() );
 +   
 +      actCellText  = $('.select-icon').find(':selected').text() ;
 +      actCellValue = $(".select-icon").val(); 
 +
 +      $( ".select-icon" ).select2('destroy');
 +      $( ".select-icon" ).remove();
 +    
 +      setCellvalue( actCell, actCellText, actCellValue);
 +    }
 +    
 +    // reset all
 +    actCell={};
 +    secondClick=false;
 +    
 +}
 +</code>
 +
 +Die eigentliche Liste implementieren:
 +
 +<code javascript>
 +//---------
 +//create new Box at $(this.triggeringElement).closest('td'
 +function initSelect2( cell , triggerElem ) {
 +    
 +    // trigger only if first time clicked
 +    if (lastTriggerElement == triggerElem ) {
 +        console.log("Same Element !");
 +        console.log(triggerElem);
 +    }
 +    else {
 +    
 +        lastTriggerElement=triggerElem;
 +       
 +         //remeber the old values
 +        
 +        lastCellValue=actCellValue;   
 +        lastCellText=actCellText; 
 +        
 +        actCellValue=getCellValue( $(triggerElem).closest('td'));
 +        actCellText=getCellText(   $(triggerElem).closest('td'));
 +
 +        //remenber the last edit cell from the history
 +        lastCell=actCell;     //( actCell ? actCell : cell  );
 +        
 +        
 +        // remove all other boxes on the page
 +        
 +        lastCellText  = $('.select-icon').find(':selected').text() ;
 +        lastCellValue = $(".select-icon").val(); 
 +       
 +        
 +        $(".select-icon").select2('destroy');
 +        $(".select-icon").remove();
 +        
 +
 +
 +       
 +        //set the value on the last cell
 +        // only if value extis (not first time)
 +        if (secondClick) {
 +            setCellvalue( lastCell, lastCellText, lastCellValue );    
 +        }
 +        else {
 +            secondClick=true;
 +        }
 +        
 +        //remeber the actual values of in the globals
 +        actCell=cell;
 +       
 +         //clear this cell    
 +        setCellvalue( cell, "" , "" );
 +        
 +        // set at the first java Script option 
 +        console.log("Remember akt values t::"+ actCellText + " class:"+actCellValue);
 +       
 +        // add box to cell
 +        cell.append(iconSelectList);
 +
 +        //create select2 box
 +        $('.select-icon').select2({      
 +                  width:         "100%"
 +                  minimumResultsForSearch: Infinity            , 
 +                   allowClear:          true                    , 
 +                   placeholder:    'Icon auswählen'      , 
 +                  templateResult:     setListEntry              , 
 +                  templateSelection:  returnSelection           ,
 +                  //dropdownCssClass:   'iconFont'                 ,                   
 +                  escapeMarkup:       function(m) { return m;  }
 +        });
 +
 +        //set the list to the actual value
 +        console.log("set pre selected value  t::"+ actCellText + " class:"+actCellValue);
 +        $('.select-icon').val(actCellValue).trigger('change');
 +    }
 +}
 +</code>
 +
 +
 +----
 +
 +=== Tastatur Handling mit berücksichtigen===
 +
 +Nach der Auswahl eines Elements soll ja die Select2 Liste wieder entfernt und das Icon angezeigt werden.
 +
 +Tastatur Event Handler definieren:
 +
 +<code javascript>
 +
 +//---------
 +//
 +// Key Handling
 +//
 +
 +document.onkeydown = TabExample;
 +
 +function TabExample(evt) {
 +  var evt = (evt) ? evt : ((event) ? event : null);
 +  var tabKey = 9;
 +  if(evt.keyCode == tabKey) {
 +    clearAll(this);
 +  }
 +}
 +
 +</code>
 +
 +
 +----
 +
 +
 +=== Auswahl Liste in dem Bericht beim Click auf eine Zelle aktiveren ===
 +
 +
 +Über eine Dynamic Action auf einen JQuery Selector den Java Skript Aufruf bei einem Click in eine Zelle aktivieren.
 +
 +JQuery Selctor für alle Zellen ist hier bei: **[headers="ICON_DISPLAY"]**
 +
 +
 +Zuerst wird optisch markiert, in welcher Zeile wir etwas editiert haben:
 +
 +<code javascript>
 +    // mark the triggered element
 +    if (lastTriggerElement == this.triggeringElement ) {
 +        
 +        console.log("Same Element !");
 +    }
 +    else {
 +    
 +        
 +    // the complete line
 +    $(this.triggeringElement).closest('tr').css({
 +        "background-color": "#fbfbdf"
 +    });
 +
 +    //the box
 +    $(this.triggeringElement).closest('td').css({
 +        "background-color": "#e5f3e5"
 +    });
 +           
 +        
 +}
 +
 +</code>
 +
 +
 +Select2 Liste einfügen:
 +
 +<code javascript>
 +// create select2 Element 
 +initSelect2( $(this.triggeringElement).closest('td') ,this.triggeringElement);
 +</code>
 +
 +=== Auswahl Liste wieder deaktiveren ===
 +
 +Klickt der Anwender in die nächste Zelle soll die Liste wieder ausgeblendet werden.
 +
 +Dynamic Action anlegen mit JQuery Selector auf die anderen Spalten der Tabelle wie : [headers="RAUMNR"] , [ headers="RAUM"] , [headers="ICON_NAME"
 +
 +JavaScript code:
 +
 +<code javascript>
 +// clear the last entries
 +clearAll( $(this.triggeringElement) );
 +</code>
 +
 +Die gleiche JavaScript Methode wird auch beim Save Button etc. aufgerufen um die Liste wieder zu schließen.
 +
 +----
 +
 +=== Speicher der Auswahl / Änderungen ===
 +
 +Über eine Button "Speichern" wird die geänderte Tabelle in der Datenbank gespeichert.
 +
 +
 +Vorbereitung:
 +
 +  * Save Button anlegen
 +  * Hidden Item für die zu übertragenden Werte anlegen ( diese müssen die Eigenschaft "Value Protected auf "No" besitzen!)
 +  * Java Script Block über das Übertragen des Modells in die Hidden Items ( Liste mit ":" separiert pro Wert )
 +  * Dynamic Action für das Speichern der Daten erzeugen
 +    * JavaScript Block um die Tabelle auszuwerten und in das Modell zu übertragen
 +    * PL/SQL Block um die Änderungen an die DB zu übertragen
 +    * JavaScript Block um eine Meldung anzuzeigen
 +
 +
 +
 +== Funktion um notwendigen Werte aus dem Modell in die Hidden Items zu übertragen==
 +
 +Code um die Werte aus dem Modell auch auszulesen (In dem Java Script Block der Seite hinterlegen):
 +<code javascript>
 +function setItemValues(dataset){
 +    //clear old entries
 +    $s('P322_SUMBIT_ROOM_LIST','');
 +    $s('P322_SUBMIT_ICON_LIST','');
 +    
 +    //loop over the dataset
 +    dataset.forEach (
 +         
 +     function (data_row)  {
 +           //get Room Nr
 +           akt_val_raumnr=$v('P322_SUMBIT_ROOM_LIST');
 +           elem_raumnr=data_row['RAUMNR'];
 +           
 +          
 +          elem_icon=data_row['ICON_DISPLAY'];
 +         
 +         if ( elem_icon != '-' &&  elem_icon.length > 0  && elem_icon != 'null' ) {
 +             
 +              akt_val_icon=$v('P322_SUBMIT_ICON_LIST');
 +              
 +              // add icon to list
 +              akt_val_icon+=elem_icon+':'
 +              $s('P322_SUBMIT_ICON_LIST',akt_val_icon);
 +
 +              //add room to list
 +              akt_val_raumnr+=elem_raumnr+':'
 +                           
 +              $s('P322_SUMBIT_ROOM_LIST',akt_val_raumnr);
 +              
 +             console.log(" Room Nr  :: " + elem_raumnr + ' ICON :: '+elem_icon);
 +          }
 +           
 +         }
 +    );
 +    
 +    
 +}
 +
 +
 +</code>
 +Dieser Teil ist nicht parametrisiert und muss dann auf die jeweiligen Gegebenheiten angepasst werden.
 +
 +
 +
 +----
 +
 +=== Dynamic Action anlegen===
 +
 +Die Dyanamic Action zum Speichern besteht aus 3 Blöcken, Werte einsammeln, Werte übertragen und in die DB schreiben, Ergebnis-Meldung anzeigen.
 +
 +==Ablauf Java Script Block 1 ==
 +
 +  - Modell auslesen 
 +  - Daten aus den Modell in die entsprechenden Page Items schreiben, diese müssen die Eigenschaft "Value Protected auf "No" besitzen!
 +
 +
 +
 +Java Script Code für das Setzen der Page Items 
 +
 +<code javascript>
 +
 +// clear the last select2 list if still open
 +clearAll( $(this.triggeringElement) );
 +
 +
 +//-----------------------------------
 +// first read again the dataset
 +// dataset
 +// variable defined in the main part of the page!
 +//
 +dataset = getDataSet($('[headers="'+headerColumnName+'"]'),'ROWNUM');
 +
 +// copy the values to the submit items
 +setItemValues(dataset);
 +
 +
 +//remove last : if extis
 +
 +akt_val_raumnr=$v('P322_SUMBIT_ROOM_LIST');
 +akt_val_icon=$v('P322_SUBMIT_ICON_LIST');
 +           
 +             
 +if(akt_val_raumnr.slice(-1) == ":"){
 +    akt_val_raumnr = akt_val_raumnr.slice(0,-1)+ "";
 +}
 +
 +if(akt_val_icon.slice(-1) == ":"){
 +    akt_val_icon = akt_val_icon.slice(0,-1)+ "";
 +}
 +
 +$s('P322_SUBMIT_ICON_LIST',akt_val_icon);
 +$s('P322_SUMBIT_ROOM_LIST',akt_val_raumnr);
 +
 +
 +//debug
 +console.log( 'P322_SUMBIT_ROOM_LIST :: ' + $v('P322_SUMBIT_ROOM_LIST') );
 +console.log( 'P322_SUBMIT_ICON_LIST :: ' + $v('P322_SUBMIT_ICON_LIST') );
 +console.log( 'P322_DISPLAY          :: ' + $v('P322_DISPLAY') );
 +
 +</code>
 +
 +
 +== PL/SQL Block für das eigentliche Speichern der Daten ==
 +
 +Die Daten werden als ":" separierter Text String übertragen! Und dann in PL/SQL mit Hilfe von "apex_string.split" wieder in ein Array zurück übertragen.
 +
 +
 +Für das Fehlerhandling im PL/SQL Block zwei Hidden Item anlegen:
 +
 +  *   P322_MESSAGE  (Value Protected auf No!)
 +  *   P322_MESSAGE_CODE (Value Protected auf No!)
 +
 +
 +In der Dynamic Action:
 +
 +<code pl/sql>
 +declare
 +
 + v_message varchar2(4000);
 + v_message_code pls_integer:=0;
 +
 +begin
 + p_aus_masterdata.set_display_room_assigments( p_display =>  :P322_DISPLAY
 +                                     , p_room_group_list =>  :P322_SUMBIT_ROOM_LIST
 +                                     , p_room_icon_list  =>  :P322_SUBMIT_ICON_LIST 
 +                                     ,  p_message        => v_message
 +                                     ,  p_message_code   => v_message_code) ;
 +
 + 
 +  :P322_MESSAGE := v_message;
 +  :P322_MESSAGE_CODE :=v_message_code;
 +end;
 +</code>
 +
 +Darauf auchten das bei "ITEMS to Submit" auch alle notwendigen ITEMs eingetragen werden und bei "Items to Return" die beiden Message Items hintelegt sind!
 +
 +
 +
 +In der Datenbank (in Auszügen):
 +
 +<code plsql>
 +procedure set_display_room_assigments( p_display number 
 +                                     , p_room_group_list varchar2
 +                                     , p_room_icon_list varchar2
 +                                     , p_message      out varchar2
 +                                     , p_message_code out integer) 
 +is
 +
 +    v_routine_name   VARCHAR2 (50) :=    g_pck  || '.set_display_room_assigments';
 +    v_count integer;
 +    v_icon_array wwv_flow_t_varchar2;
 +    v_room_array wwv_flow_t_varchar2;
 +  
 +    v_icon_name varchar2(256);
 +    v_room_pk number(11);
 +    
 +    v_message_code pls_integer:=0;
 +    v_message_text varchar2(4000):='Raumzuordnung gespeichert'; 
 +   
 +begin
 +
 +  
 +  v_icon_array := apex_string.split( p_room_icon_list, ':' );
 +  v_room_array := apex_string.split( p_room_group_list, ':' );
 +   
 +  delete from RaumDisplays where DISPLAY_ID=p_display;
 +  
 +  
 +   if v_room_array.count  > 0 then
 +        for i in v_room_array.first .. v_room_array.last
 +        loop
 +          if v_room_array.exists(i) then
 +          
 +            -- get the pk of a room
 +            select  ... into v_room_pk
 +             where xxxxr=v_room_array(i);
 +          
 +          end if;
 +          if v_icon_array.exists(i) then          
 +                  insert into RaumDisplays ( ID, DISPLAY_ID, ICON_NAME, SCHULRAUM_OID ) 
 +                   values
 +                   ( RaumDisplays_seq.nextval
 +                   , p_display
 +                   , v_icon_array(i)
 +                   , v_room_pk);
 +            end if;   
 +   
 +        end loop;
 +   end if;
 +   
 +   commit;    
 +   
 +   p_message_code:    v_message_code;
 +   p_message     :    v_message_text;
 +   
 +exception
 +    when others then
 +    p_message      := 'Error : Update Room to Display Assigment failed :: ' ||SQLERRM;
 +    p_message_code :=  SQLCODE;
 +    raise_application_error(-20001, p_message);  
 +                  
 +                                    
 +end set_display_room_assigments;
 +
 +
 +
 +</code>
 +
 +
 +== JavaScript Block für das Anzeigen einer Meldung ==
 +
 +Da "apex_application.g_print_success_message" nur beim Rendern der Seite funktioniert ( z.b. in einem Prozess in PL/SQL) muss eine inline Message über die API verwendet werden.
 +
 +
 +Nächste Action in Java Script:
 +
 +<code javascript>
 +apex.message.clearErrors();
 +
 +v_message     =$v('P322_MESSAGE');
 +v_message_code=$v('P322_MESSAGE_CODE');
 +
 +if ( v_message_code != '0' ) {
 +
 +    apex.message.showErrors([
 +      {
 +        type: apex.message.TYPE.ERROR,
 +        location: ["page"],
 +        message: v_message,
 +        unsafe: false
 +      }
 +    ]);
 +
 +else { 
 +  apex.message.showPageSuccess( v_message );
 +}
 +</code>
 +  
 +Beim Speichern wird dann eine entsprechende Box mit einer Meldung aus der DB angezeigt, hier ein Fehler:
 +
 +{{ :prog:apex:apex_error_message_inline.png | Oracle Apex Inline Error Message JavaScript API apex.message.showErrors }}
 +
 +
 +----
 +
 +=== Optimierungspotential ===
 +
 +  * Schöners und moderners JavaScript ( man ist halt doch pl/sql entwickler 8-) )
 +  * Weniger Abhängigkeiten zu globalen Variablen 
 +  * Mehr Parametrisierung um das einfacher auf andere Anwendungsfälle zu übertragen
 +
 +CSS Spielereien:
 +
 +<code css>
 +
 +//Breite anpassen
 +.select2-wrapper {
 +    width: 200px;
 +}
 +
 +// Größe und Position anpassen
 +.select2-results .fa {
 +    float: left;
 +    position: relative;
 +    line-height: 20px;
 +}
 +
 +
 +</code>
 +
 +----
 +  
 +  
 +==== Quellen====
 +
 +Select2
 +  * https://select2.org/
 +
 +APEX Inline Message
 +
 +  * https://www.talkapex.com/2018/03/custom-apex-notification-messages/
prog/apex_select2_ajax.txt · Zuletzt geändert: 2019/01/14 12:37 von gpipperr