— Roel Hartman (@RoelH) April 26, 2016
Real Hardware issues on the software conference #APEXconn16. And still calm: @anjeli2001 congrats! pic.twitter.com/4ahCZzh5gn— Stephan La Rocca (@StephanLaRocca) April 26, 2016
— Roel Hartman (@RoelH) April 26, 2016
Real Hardware issues on the software conference #APEXconn16. And still calm: @anjeli2001 congrats! pic.twitter.com/4ahCZzh5gn— Stephan La Rocca (@StephanLaRocca) April 26, 2016
<br />select dname as CATEGORY<br /> , empno as ID<br /> , ename as NAME<br /> , rank() over (partition by emp.deptno order by empno asc) as ROW_ASC<br />from emp, dept<br />where emp.deptno=dept.deptno<br />
<br />{"text":"#CATEGORY#","expanded":true,"leaf":false,"children":[<br />{"text":"#NAME#","id":"#ID#","leaf":true}<br /><br />#ROW_ASC#=1 and #ROWNUM#=1<br />
<br />]},{"text":"#CATEGORY#","expanded":true,"leaf":false,"children":[<br />{"text":"#NAME#","id":"#ID#","leaf":true}<br /><br />#ROW_ASC#=1 and #ROWNUM#!=1<br />
<br />,{"text":"#NAME#","id":"#ID#","leaf":true}<br /><br />#ROW_ASC#!=1<br />
<br /><div id="tree-div#REGION_ID#">[<br />
<br />]}]</div><br /><script type="text/javascript"><br /> Ext.onReady(function(){<br /> initTreeRegion('#REGION_ID#');<br /> })<br /></script><br /><br />function initTreeRegion(pId) {<br /> var myDiv = $x('tree-div'+pId);<br /> var myData = Ext.util.JSON.decode(myDiv.innerHTML); <br /> myDiv.innerHTML = '';<br /> new Ext.tree.TreePanel({<br /> renderTo: 'tree-div'+pId,<br /> border:false,<br /> id: 'REGION_'+pId+'_tree',<br /> cls: 'myTreePanel',<br /> margins: '2 2 0 2',<br /> autoScroll: true,<br /> rootVisible: false,<br /> root:{ "text": "rootnode",<br /> "expanded": true,<br /> "children": myData <br /> }<br /> });<br />}<br /><br />$a_report('9159609248948926','1','100');<br />initTreeRegion('R9159609248948926');<br />
var propsGrid = new Ext.grid.PropertyGrid({
renderTo: 'prop-grid',
width: 300,
autoHeight: true,
source: {
'(name)': 'Properties Grid',
grouping: false,
autoFitColumns: true,
productionQuality: false,
created: new Date(Date.parse('10/15/2006')),
tested: false,
version: 0.01,
borderWidth: 1
},
viewConfig : {
forceFit: true,
scrollOffset: 2 // the grid will never have scrollbars
}
});
<div id="prop-grid#REGION_STATIC_ID#"></div>
<script type="text/javascript">
Ext.override(Ext.grid.PropertyGrid, {
removeProperty: function(property){
delete this.source[property];
var r = this.store.getById(property);
if(r){
// remove property
this.store.remove(r);
}
}
});
function initRegion#REGION_STATIC_ID#() {
var propsGrid = new Ext.grid.PropertyGrid({
applyTo: 'prop-grid#REGION_STATIC_ID#',
id: 'myPropertyGrid#REGION_STATIC_ID#',
width: 300,
autoHeight: true,
source: {
'#COLUMN_HEADER#':'#COLUMN_VALUE#',
'MYDUMMY123':''},
viewConfig : {
forceFit: true,
scrollOffset: 2 // the grid will never have scrollbars
}
});
propsGrid.removeProperty('MYDUMMY123'); // remove the dummy entry
}
Ext.onReady(function(){
initRegion#REGION_STATIC_ID#();
});
</script>
select '01.06.1980' as "Birthday"
, 'Anja' as "Firstname"
, 'Hildebrandt' as "Name"
, 'Developer' as "Profession"
from dual
<br />{ "text" : "#CATEGORY#"<br /> , "expanded" : true<br /> , "leaf" : false<br /> , "children" : [<br /> { "text" : "#NAME#"<br /> , "id" : "#ID#"<br /> , "leaf" : true<br /> }<br /><br />#ROW_ASC#=1 and #ROWNUM#=1<br />
<br />]},{ "text" : "#CATEGORY#"<br /> , "expanded" : true<br /> , "leaf" : false<br /> , "children" : [<br /> { "text" : "#NAME#"<br /> , "id" : "#ID#"<br /> , "leaf" : true<br /> }<br /><br />#ROW_ASC#=1 and #ROWNUM#!=1<br />
<br />,{ "text" : "#NAME#"<br /> , "id" : "#ID#"<br /> , "leaf" : true<br /> }<br /><br />#ROW_ASC#!=1<br />
<br /><div id="tree-div#REGION_STATIC_ID#"></div><br /><script type="text/javascript"><br />Ext.onReady(function(){<br /> new Ext.tree.TreePanel({<br /> renderTo : 'tree-div#REGION_STATIC_ID#',<br /> border : false,<br /> id : 'tree-panel',<br /> margins : '2 2 0 2',<br /> autoScroll : true,<br /> rootVisible : false,<br /> root:{ <br /> "text" : "rootnode",<br /> "expanded" : true,<br /> "children" : [<br /><br /> ]} <br /> ]}<br /> }); // new Ext.tree.TreePanel... <br />}); //Ext.onReady...<br /><br /></script><br />
<br />select dname as CATEGORY<br /> , empno as ID<br /> , ename as NAME<br /> , rank() over (partition by emp.deptno order by empno asc) as ROW_ASC<br />from emp, dept<br />where emp.deptno=dept.deptno<br />
[{
xtype: 'box',
el: 'my_reg_pos_08'
},'->'#BAR_BODY#]
navigation bar entry,{
xtype: 'box',
autoEl: {
tag: 'a',
href: '#LINK#',
cn: '<img src="#IMAGE#" alt="#TEXT#" />',
title : '#TEXT#'
}
},{xtype: 'tbspacer', width: 10}
As you can see, I build the complete json string to define my toolbar items.... I also include one of my region-position-divs (defined in the body below), because i need a bit of form-magic inside my toolbar....<div id="my_nav_bar"></div>I don't use any usual html table layout. I only define a bunch of div regions according to the region positions, messages, navigation bar etc...
<div id="myTabs"></div>
<div id="my_messages">#GLOBAL_NOTIFICATION##SUCCESS_MESSAGE##NOTIFICATION_MESSAGE#</div>
<div id="my_box_body" width="100%">#BOX_BODY#</div>
<div id="my_reg_pos_01">#REGION_POSITION_01#</div>
<div id="my_reg_pos_02">#REGION_POSITION_02#</div>
<div id="my_reg_pos_03">#REGION_POSITION_03#</div>
<div id="my_reg_pos_04">#REGION_POSITION_04#</div>
<div id="my_reg_pos_05">#REGION_POSITION_05#</div>
<div id="my_reg_pos_06">#REGION_POSITION_06#</div>
<div id="my_reg_pos_07">#REGION_POSITION_07#</div>
<div id="my_reg_pos_08">#REGION_POSITION_08#</div>
<script type="text/javascript">
Ext.onReady(function(){
var nb = #NAVIGATION_BAR#;
var tb = new Ext.Toolbar({renderTo: 'my_nav_bar', items: nb});
var myform = new Ext.ux.FormViewport({
layout:'border',
items:[new Ext.Panel({
region:'north',
id: 'my_toolbar',
height: 40,
collapsible: false,
margins:'0 0 0 0',
allowDomMove:false,
tbar: tb
}),new Ext.Panel({
region:'south',
id: 'footer',
height: 22,
collapsible: false,
margins:'0 0 0 0',
html:'<div id="DeveloperToolbar"></div>'
}),
new Ext.Panel({
region:'west',
id:'my_info',
title:'Info',
split:true,
width: 220,
minSize: 175,
maxSize: 400,
collapsible: true,
margins:'40 0 5 5',
cmargins:'40 0 5 5',
contentEl: 'my_reg_pos_01',
layoutConfig:{
animate:true
}
}),new Ext.Panel({region:'center',
margins:'40 5 5 5',
id: 'my_workarea',
items: [tabs],
layout : "fit"
})
]
});
});
</script>
var nb = #NAVIGATION_BAR#;Then i use nb as item parameter for my toolbar object ...
var tb = new Ext.Toolbar({renderTo: 'my_nav_bar', items: nb});After that i am able to use tb in my actual page layout.... If everything works, it shoul look something like this.... #BOX_BODY#
Zunächst mal ein paar Worte zu ExtJS. ExtJS ist ein clientseitiges Javascript bzw. AJAX-Framework, das browserunabhängige Funktionalitäten für die DOM-Manipulation, Event-Handling oder auch asynchrone Serverkommunikation zur Verfügung stellt. Die API von ExtJS ist leicht erlernbar und wirklich intuitiv nutzbar, da sie fast vollständig JSON gestützt ist.
Für das folgende Beispiel wurde eine APEX-Anwendung mit einer Seite erstellt. Innerhalb dieser Seite gibt es eine einfache Region, die als Inhalt eine DIV-Region mit der Id “tree” definiert.
Folgende Stylesheets und Javascript-Dateien aus dem ExtJS-Package sollten im Headerbereich eingebunden sein.
<link rel="stylesheet" type="text/css" href="#IMAGE_PREFIX#javascript/ext-2.2.1/resources/css/ext-all.css" />
<script type="text/javascript" src="#IMAGE_PREFIX#javascript/ext-2.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="#IMAGE_PREFIX#javascript/ext-2.2.1/ext-all.js"></script>
Ext.onReady(function(){
new Ext.Panel({
title: 'My first tree',
renderTo: 'tree',
layout: 'border',
width: 500,
height: 500,
items: [{
xtype: 'treepanel',
id: 'tree-panel',
region: 'center',
margins: '2 2 0 2',
autoScroll: true,
rootVisible: false,
root: { text: "rootnode",
expanded: true,
children: [{ text:"node 1",
id:1,
leaf:true
},
{ text: "node 2",
id: 2,
leaf:false,
expanded: true,
children:[{ text:"node 1",
id:3,
leaf:true
}
]
}
]
},
}]
});
});Was passiert hier? Ganz einfach…
Wenn die Seite vollständig geladen ist, wird die onReady-Funktion ausgeführt. Innerhalb dieser Funktion wird ein neues ExtJS-Panel mit dem Titel “My first tree” generiert. Die Option renderTo gibt an, in welchem Bereich der Seite das neu erstellte Panel eingefügt wird. In diesem Beispiel die in unserer Seite definierte DIV-Region.
Innerhalb dieses Panels wird ein TreePanel generiert, was einen nicht sichtbaren (rootVisible:false) Root-Knoten bekommt, der zwei Unterknoten (children) “node 1” und “node 2” enthält. “node 2” wiederum hat einen weiteren Unterknoten.
Die entscheidenden Werte zur Definition einen Knoten sind:
text: Der Text der für den betreffenden Knoten im Baum angezeigt werden soll.
id: Eine eindeutige Bezeichnung für den Knoten, z.B. die tatsächliche numerische ID einer Kategorie oder ähnliches.
leaf: Diese Option gibt an, ob der betreffende Knoten ein Blatt ist oder ob er weiter Unterknoten enthält.
children: Ist ein Array der Unterknoten, sofern vorhanden.
href: Muss gesetzt werden, falls der Knoten als Link funktionieren soll.
Nun hat man natürlich eher selten eine feste Baumstruktur abzubilden. Normalerweise wollen wir eine dynamische Struktur aus der Datenbank laden.
Hierzu erstellen wir eine Datenbank-Funktion, die das JSON-Objekt als Text zurückgibt. Da es relativ mühselig wäre die dafür benötigte Struktur zusammenzubauen, bin ich mal auf die Suche nach einer fertigen Lösung gegangen. Und siehe da, es gibt bereits ein JSON-Datentyp für Oracle. Vielen Dank an Lewis Cunningham dafür.
Ok, bereiten wir also erstmal ein paar Testdaten und die benötigte Funktion auf der Datenbank vor.
create table tbl_mon_category (cat_id number, cat_par_id number, cat_label varchar2(100));
insert into tbl_mon_category (cat_id,cat_par_id,cat_label) values (1,0,'Node 1');
insert into tbl_mon_category (cat_id,cat_par_id,cat_label) values (2,0,'Node 2');
insert into tbl_mon_category (cat_id,cat_par_id,cat_label) values (3,1,'Childnode A for Node 1');
insert into tbl_mon_category (cat_id,cat_par_id,cat_label) values (4,2,'Childnode A for Node 2');
insert into tbl_mon_category (cat_id,cat_par_id,cat_label) values (5,2,'Childnode B for Node 2');
insert into tbl_mon_category (cat_id,cat_par_id,cat_label) values (6,2,'Childnode C for Node 2');
commit;
create or replace package PCK_EXTJS_JSON_EXAMPLE is
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : Prüfen, ob eine Kategorie und Unterkategorien hat
function hasChildren(i_cat_id in number) return boolean;
-- Purpose : anhand einer übergebenen Kategorie-ID das
-- vollständige JSON-Objekt generieren (nutzt
-- Datentyp PL/JSON; arbeitet rekursiv!)
function getJsonObject(i_cat_id in number default 0) return json;
-- Purpose : Aufruf der Funktion zum Generieren des
-- JSON-Objekts und Rückgabe des Ergebnisses
-- als String
function getTreeDataDynamic return varchar2;
end PCK_EXTJS_JSON_EXAMPLE;
/
create or replace package body PCK_EXTJS_JSON_EXAMPLE is
/*
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : Prüfen, ob eine Kategorie und Unterkategorien hat
*/
function hasChildren(i_cat_id in number) return boolean is
v_count number:=0;
begin
select nvl(count(*),0) into v_count from tbl_mon_category where cat_par_id=i_cat_id;
if v_count>0 then
return true;
else
return false;
end if;
end;
/*
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : anhand einer übergebenen Kategorie-ID das vollständige JSON-Objekt generieren
-- (nutzt Datentyp PL/JSON)
-- ACHTUNG! REKURSIV!
*/
function getJsonObject(i_cat_id in number default 0) return json is
v_json json:=json();
v_arr_id NUMBER;
v_ele_id NUMBER;
begin
if i_cat_id = 0 then -- wenn Kategorie 0 dann wird das der Root-Knoten mit festen Werten
v_json.add_member(p_name => 'id', p_value => i_cat_id);
v_json.add_member(p_name => 'text', p_value => 'root');
else -- sonst regulärer Knoten; Werte für die Kategorie benutzen
for cat in (select * from tbl_mon_category where cat_id=i_cat_id) loop
v_json.add_member(p_name => 'id', p_value => i_cat_id);
v_json.add_member(p_name => 'text', p_value => cat.cat_label);
end loop;
end if;
if not hasChildren(i_cat_id) then -- wenn Knoten keine Child-Knoten hat
v_json.add_member(p_name => 'leaf', p_value => true); -- als Blatt markieren und fertig
v_json.add_member(p_name => 'href', p_value => 'f?p=108:2:'||v('APP_SESSION')); -- einen Link setzen
else -- sonst
v_json.add_member(p_name => 'leaf', p_value => false); -- als Knoten mit Unterknoten kennzeichnen
v_json.add_array(p_name => 'children', p_array_id => v_arr_id ); -- Array für Unterknoten hinzufügen
for child in (select * from tbl_mon_category where cat_par_id=i_cat_id) loop -- Schleife über alle Unterkategorien
-- Unterknoten über rekursiven Aufruf generieren und ins Array hängen
v_json.add_array_element(p_array_id => v_arr_id, p_value => getJsonObject(i_cat_id => child.cat_id), p_element_id => v_ele_id);
end loop;
end if;
return v_json;
end;
/*
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : Aufruf der Funktion zum Generieren des JSON-Objekts und Rückgabe des Ergebnisses als String
*/
function getTreeDataDynamic return varchar2 is
v_json json:=json();
begin
v_json:=getJsonObject(i_cat_id => 0);
return(v_json.getString);
end;
end PCK_EXTJS_JSON_EXAMPLE;
declare
v_json varchar2(32767);
begin
v_json:=PCK_EXTJS_JSON_EXAMPLE.getTreeDataDynamic;
htp.prn(v_json);
end;
Ext.onReady(
function(){
var TreeRequest = new htmldb_Get(null,$v('pFlowId'),'APPLICATION_PROCESS=getTreeData',0);
var TreeResponse = TreeRequest.get();
if (TreeResponse) {
var v_extjs_tree_data= Ext.util.JSON.decode(TreeResponse);
}
new Ext.Panel({
title: 'My first tree',
renderTo: 'tree',
layout: 'border',
width: 500,
height: 500,
items: [{
xtype: 'treepanel',
id: 'tree-panel',
region: 'center',
margins: '2 2 0 2',
autoScroll: true,
rootVisible: false,
root: v_extjs_tree_data,
}]
});
}
);
<link rel="stylesheet" type="text/css" href="#IMAGE_PREFIX#javascript/ext-2.2.1/resources/css/ext-all.css" />
<script type="text/javascript" src="#IMAGE_PREFIX#javascript/ext-2.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="#IMAGE_PREFIX#javascript/ext-2.2.1/ext-all.js"></script>
Ext.onReady(function(){
new Ext.Panel({
title: 'My first tree',
renderTo: 'tree',
layout: 'border',
width: 500,
height: 500,
items: [{
xtype: 'treepanel',
id: 'tree-panel',
region: 'center',
margins: '2 2 0 2',
autoScroll: true,
rootVisible: false,
root: { text: "rootnode",
expanded: true,
children: [{ text:"node 1",
id:1,
leaf:true
},
{ text: "node 2",
id: 2,
leaf:false,
expanded: true,
children:[{ text:"node 1",
id:3,
leaf:true
}
]
}
]
},
}]
});
});The most important options to define a node are:
text: text to be displayed in the tree
id: id for the node, which should be unique in the tree
leaf: this option shows wether the node is a leaf or not
children: an array of childnodes if there are any
href: has to be set for the node to work as a link (line 68 next scriptblock)
create table tbl_category (cat_id number, cat_par_id number, cat_label varchar2(100));
insert into tbl_category (cat_id,cat_par_id,cat_label) values (1,0,'Node 1');
insert into tbl_category (cat_id,cat_par_id,cat_label) values (2,0,'Node 2');
insert into tbl_category (cat_id,cat_par_id,cat_label) values (3,1,'Childnode A for Node 1');
insert into tbl_category (cat_id,cat_par_id,cat_label) values (4,2,'Childnode A for Node 2');
insert into tbl_category (cat_id,cat_par_id,cat_label) values (5,2,'Childnode B for Node 2');
insert into tbl_category (cat_id,cat_par_id,cat_label) values (6,2,'Childnode C for Node 2');
commit;
create or replace package PCK_EXTJS_JSON_EXAMPLE is
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : check if category has sub-categories
function hasChildren(i_cat_id in number) return boolean;
-- Purpose : create JSON-Object for category;
-- recursive<br/> function getJsonObject(i_cat_id in number default 0) return json;
-- Purpose : function that calls getJsonObject and returns the result as string
function getTreeDataDynamic return varchar2;
end PCK_EXTJS_JSON_EXAMPLE;
/
create or replace package body PCK_EXTJS_JSON_EXAMPLE is
/*
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : check if category has sub-categories
*/
function hasChildren(i_cat_id in number) return boolean is
v_count number:=0;
begin
select nvl(count(*),0) into v_count from tbl_category where cat_par_id=i_cat_id;
if v_count>0 then
return true;
else
return false;
end if;
end;
/*
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : create JSON-Object for category;
-- recursive
*/
function getJsonObject(i_cat_id in number default 0) return json is<br/> v_json json:=json();
v_arr_id NUMBER;
v_ele_id NUMBER;
begin
if i_cat_id = 0 then -- if category is root then set root-node
v_json.add_member(p_name => 'id', p_value => i_cat_id);
v_json.add_member(p_name => 'text', p_value => 'root');
else -- else --> usual node; use category infos
for cat in (select * from tbl_category where cat_id=i_cat_id) loop
v_json.add_member(p_name => 'id', p_value => i_cat_id);
v_json.add_member(p_name => 'text', p_value => cat.cat_label);
end loop;
end if;
if not hasChildren(i_cat_id) then -- if node has no children
v_json.add_member(p_name => 'leaf', p_value => true); -- mark as leaf
v_json.add_member(p_name => 'href', p_value => 'f?p=108:2:'||v('APP_SESSION')); -- set a link if needed
else -- sonst
v_json.add_member(p_name => 'leaf', p_value => false); -- mark as node with children
v_json.add_array(p_name => 'children', p_array_id => v_arr_id ); -- add array for subcategories
for child in (select * from tbl_category where cat_par_id=i_cat_id) loop -- loop through all sub-categories
-- create JSON-object for sub-category using recursive call and the append to array
v_json.add_array_element(p_array_id => v_arr_id, p_value => getJsonObject(i_cat_id => child.cat_id), p_element_id => v_ele_id);
end loop;
end if;
return v_json;
end;
/*
-- Author : AHILDEBRANDT
-- Created : 10.08.2009
-- Purpose : function that calls getJsonObject and returns the result as string
*/
function getTreeDataDynamic return varchar2 is
v_json json:=json();
begin
v_json:=getJsonObject(i_cat_id => 0);
return(v_json.getString);
end;
end PCK_EXTJS_JSON_EXAMPLE;
/
declare
v_json varchar2(32767);
begin
v_json:=PCK_EXTJS_JSON_EXAMPLE.getTreeDataDynamic;
htp.prn(v_json);
end;
Ext.onReady(
function(){
var TreeRequest = new htmldb_Get(null,$v('pFlowId'),'APPLICATION_PROCESS=getTreeData',0);
var TreeResponse = TreeRequest.get();
if (TreeResponse) {
var v_extjs_tree_data= Ext.util.JSON.decode(TreeResponse);
}
new Ext.Panel({
title: 'My first tree',
renderTo: 'tree',
layout: 'border',
width: 500,
height: 500,
items: [{
xtype: 'treepanel',
id: 'tree-panel',
region: 'center',
margins: '2 2 0 2',
autoScroll: true,
rootVisible: false,
root: v_extjs_tree_data,
}]
});
}
);
Just got news, that there will be an apex competition this year...
http://www.oracle.com/technology/products/database/application_express/html/competition.html
You can win tickets for this years Oracle Open World, so better go now and submit your application ;) ...
Good luck...
Anja
Habe gerade einen Link zum diesjährigen internationalen APEX Wettbewerb bekommen.
http://www.oracle.com/technology/products/database/application_express/html/competition.html
Es gibt Tickets für die Open World zu gewinnen, also am besten gleich registrieren und Applikation hochladen…
Viel Erfolg
Anja
Viele kennen bestimmt das ApexLib-Framework von Patrick Wolf…. Ich bin darüber gestolpert, als ich auf der Suche nach einer Möglichkeit war, anhand der aktuell eingegebenen Daten zu entscheiden, das bestimmte Felder required sein sollen oder eben nicht. Mit Bordmitteln von Apex kam ich da nicht weiter. Selbst die ApexLib half mir zunächst nicht weiter, bis ich dann etwas Zeit fand, um mir das ganze mal näher anzusehen.
Wie funktioniert’s?
Ganz einfach! ApexLib laut Anleitung installieren und in die betreffende Anwendung integrieren. Dann kann man auch schon die diversen Hints nutzen. Zum Beispiel reicht der String “$APEXLIB_REQUIRED$" im Kommentar des Items aus damit das Framework eine hübsche Out-of-the-box-Validierung daraus baut (mit Inline-Fehlermeldung u.ä.).
Das gefiel mir gut, also sah ich mir das genauer an….
Alle Beschreibungsdaten der Seiten, Felder, Regionen u.Ä. werden in der Datenbank gespeichert. Diese Tatsache nutzt das ApexLib-Framework aus. Bei der Installation werden verschiedene Views angelegt, z.B. APEXLIB_V_PAGE_ITEM. Diese View enthält alle Daten der Seitenelemente (also auch den Kommentar). Da kam mir dann die Idee… Warum nicht einfach diese View so anpassen, das sie anhand von Daten in der aktiven Session (mit der V-Funktion kann man ja alle Werte abrufen) den Eintrag im Kommentar dynamisch erzeugt. Gesagt, getan…. und was soll ich sagen…. funktioniert perfekt…
Nachteil von der ganzen Geschichte ist natürlich, das man aufpassen muss, wenn man eine neuere Version des Frameworks installiert.
Vielleicht hilft es ja doch dem ein oder anderen weiter, der eine ähnliche Anforderung bekommen hat…
Das war’s für heute…
Anja
Und noch so ein Kundenwunsch… ;)
Der Inhalt einer normalen Werteliste kann schon mal unübersichtlich werden. Was also tun, um ein bißchen mehr Übersichtlichkeit in die Sache zu bringen? Ganz einfach: Gruppieren! Aber wie?
Auf der Suche nach einer Lösung für dieses Problem stolperte ich vor einer Weile über eine wirklich nützliche Javascript-Bibliothek genannt jQuery.
Das Ziel der ganzen Übung soll dann etwa so aussehen…
Eine normale Werteliste sieht aber normalerweise so aus….
Was also tun, um von der normalen Werteliste zur Strukturierten zu kommen?
Zunächst hab ich alle Wertelisten, die entsprechend “nachbearbeitet” werden sollen gekennzeichnet. Dazu einfach bei der Definition des Elements im Bereich HTML-Form-Elementattribute eine eigene Klasse zuweisen.
So kann man schon mal normale Wertelisten von denen unterscheiden, die entsprechend strukturiert werden sollen. Außerdem habe ich die Positionen meiner Gruppierung innerhalb der normalen Werteliste mit Einträgen (Wert “-1”) markiert.
Nun kommt jQuery ins Spiel. Folgendes Stückchen Javascript in die Seite einfügen….
<script type="text/javascript">
$(document).ready(function(){
$("select.MyGroupedLOV").each(function(i) {
var $sel=$(this);
$sel.find("option").filter("option[value='-1']").each(function(i) {
var s = $sel.find("option").index($(this));
var e = $sel.find("option").index($sel.find('option:gt('+s+')[value="-1"]:first'));
if (e < 0) e = $sel.find("option").length;
$sel.find("option").slice(s, e).wrapAll("<optgroup label='"+$(this).text()+"'></optgroup>");
}); // .. $sel.find("opt ..
$sel.find("option[value='-1']").remove();
});
});
</script>
Was passiert da? Eigentlich ganz einfach… Die Funktion parst die “option”-Listen aller gekennzeichneten “select”-Elemente und schließt alle Optionen, die nicht einen Wert von “-1” haben in eine Optionsgruppe ein. Das Label der Gruppe wird jeweils durch die Option mit Wert “-1” vor den eingeschlossenen Optionen benannt.
Es gibt natürlich noch weitere, weitaus schönere Ansätze dieses Problem zu lösen. Man könnte beispielsweise die gesammte Liste per AJAX laden, aber das könnt ihr ja selbst ausprobieren…. ;)
Dieser Thread im OTN Forum beschäftigt sich unter anderem mit der Ersetzung des normalen Datepickers und benutzt dazu einen ähnlichen Ansatz.
Kürzlich bekam ich von einem Kunden die Anforderung in einem Call Center aufgenommene Gespräche mit Hilfe eines Fragebogens auf deren Qualität hin zu bewerten. Dazu muss man sich das File natürlich anhören können. Auf der Suche nach einem kleinen leicht in eine APEX-Anwendung zu integrierenden MP3-Player stieß ich dann auf den Nifty Player.
Ein kleiner Flashplayer, der auch komplett per Javascript zu steuern ist, so dass man das Flashfrontend theoretisch auch vollständig umgehen kann. Für meine kleine Demo-Anwendung reichte dieser Player zumindest völlig aus.
Hat jemand ähnliche Player gefunden???? Ich bin immer auf der Suche nach Alternativen….
Tschüß für heute
Anja
Ja, das ist er also… Der erste Eintrag in meinem brandneuen APEX-Blog. Nach inzwischen 8 Jahren Erfahrung im Bereich Entwicklung datenbankgestützter Anwendungen und 3 Jahren Erfahrung mit Oracle Application Express wird es wohl Zeit diese Erfahrung mit der Community zu teilen. Und was wär da einfacher als mich all den anderen Blogbetreibern anzuschließen und hiermit mein eigenes Blog zu eröffnen und meinen Teil zur “Weltherrschaft ” von APEX beizutragen.
In diesem Sinne, viel Spaß beim Lesen!