Si bien Oracle Application Express nos ofrece muchos tipos de Informes para ser usados cuando estamos creando nuestras aplicaciones web, pero a veces no se ajusta a lo que necesitamos, por ejemplo puede darse el caso de que tengamos una aplicación construida con un módulo PL/SQL y queremos reutilizar ese módulo, entonces para ello necesitamos crear un informe de tipo PL/SQL dinámico.

En mi opinión, nunca está de más saber cómo podemos crear en Oracle Apex Informes para mostrar datos de una tabla en forma dinámica, usando código PL/SQL, pero, hay que ser conscientes, de que se requiere de mucho detalle y hay que plantearse si es conveniente o no utilizar este tipo de Informes, ya que con los asistentes de Apex podemos crear Informes de forma muy rápida y sencilla, sin tanto esfuerzo.

Vamos a trabajar con la tabla DEMO_CUSTOMERS,  que pertenece a la aplicación demo “Sample Database Application” de Apex cuando se instala.

Para mostrar los datos de la tabla en un informe creado dinámicamente vamos a hacer uso del paquete APEX_ITEM que nos permite crear elementos dinámicamente basados en una consulta SQL.

En nuestra consulta SQL vamos a hacer uso de dos tipos de funciones:

  1. HIDDEN: Usamos esta función para generar dinámicamente elementos ocultos.
APEX_ITEM.HIDDEN(
    p_idx         IN    NUMBER,
    p_value       IN    VARCHAR2 DEFAULT
    p_attributes  IN    VARCHAR2 DEFAULT NULL,
    p_item_id     IN    VARCHAR2 DEFAULT NULL,
    p_item_label  IN    VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2;

2. DISPLAY_AND_SAVE: Usamos esta función para mostrar un elemento como texto, pero guardamos su valor en el estado de sesión.

APEX_ITEM.DISPLAY_AND_SAVE(    
p_idx         IN    NUMBER,    
p_value       IN    VARCHAR2 DEFAULT NULL,    
p_item_id     IN    VARCHAR2 DEFAULT NULL,    
p_item_label  IN    VARCHAR2 DEFAULT NULL)    
RETURN VARCHAR2;

p_idx: Indica el número de identificador de la variable htmldb_application_global. El rango de valores es de 1 a 50. Por ejemplo 1 crea F01 y 2 crea F02.

Además, para que se dibuje la información en la región vamos a hacer uso de dos paquetes: HTP o HTF

Paquete htp: (Procedimientos de hipertexto) – Son procedimientos que generan etiquetas HTML. Por ejemplo, el procedimiento htp.htmlopen genera la etiqueta de apertura <HTML>.

Paquete htf: Para cada procedimiento htp que genera una etiqueta HTML hay una función htf con idénticos parámetros. Las funciones no generan directamente la salida en una página web. En lugar de eso, pasan su salida como valores de retorno a los estados que le han sido invocados.

Crear Región de Contenido Dinámico PL/SQL

Creamos una aplicación de escritorio, luego creamos una página en blanco y en ella creamos una región de contenido dinámico PL/SQL que la llamaremos “Clientes”.

Lo más importante aquí es el código PL/SQL en esta región ya que es la que se encargará de armar el Informe en forma dinámica.

Ingresamos el código PL/SQL y hacemos clic en el botón Guardar.

declare
cursor c_cus
is
select apex_item.hidden (1,cus.customer_id) id
, apex_item.display_and_save (2, cus.cust_first_name) firstname
, apex_item.display_and_save (3, cus.cust_last_name) lastname
, apex_item.display_and_save (4, cus.cust_street_address1) address
, apex_item.display_and_save (5, cus.cust_city) city
, apex_item.display_and_save (6, cus.cust_state) state
, apex_item.display_and_save (7, cus.cust_postal_code) postalcode
, apex_item.display_and_save (8, cus.cust_email) email
, apex_item.display_and_save (9, phone_number1) phonenumber
, apex_item.display_and_save (10, credit_limit) creditlimit
from demo_customers cus
, demo_states sta
where cus.cust_state = sta.st
order by cus.customer_id;
begin
for r_cus in c_cus
loop
htp.p(r_cus.id);
htp.p(r_cus.firstname);
htp.p(r_cus.lastname);
htp.p(r_cus.address);
htp.p(r_cus.city);
htp.p(r_cus.state);
htp.p(r_cus.postalcode);
htp.p(r_cus.email);
htp.p(r_cus.phonenumber);
htp.p(r_cus.creditlimit);
end loop;
end;

Si ejecutamos la página podremos ver el reporte sin ningún formato, además todos los registros se muestran uno a continuación del otro.

Crear tabla HTML desde el código PL/SQL

Para crear la tabla haremos uso del paquete HTP.

htp.tableopen     -- genera la etiqueta <TABLE>
htp.tablerowopen -- genera la etiqueta <TR>
htp.tablerowclose -- genera la etiqueta </TR>
htp.tableclose    -- genera la etiqueta </TABLE>

Para ello vamos a cambiar la parte del código que está dentro del bloque begin-end, agregando la tabla y una fila para cada registro, por el hecho que está dentro del loop.

declare
cursor c_cus
is
select apex_item.display_and_save(1, cus.customer_id) id
, apex_item.display_and_save (2, cus.cust_first_name) firstname
, apex_item.display_and_save (3, cus.cust_last_name) lastname
, apex_item.display_and_save (4, cus.cust_street_address1) address
, apex_item.display_and_save (5, cus.cust_city) city
, apex_item.display_and_save (6, cus.cust_state) state
, apex_item.display_and_save (7, cus.cust_postal_code) postalcode
, apex_item.display_and_save (8, cus.cust_email) email
, apex_item.display_and_save (9, phone_number1) phonenumber
, apex_item.display_and_save (10, credit_limit) creditlimit
from demo_customers cus
, demo_states sta
where cus.cust_state = sta.st
order by cus.customer_id;
begin
htp.tableopen; -- genera la etiqueta <TABLE>
for r_cus in c_cus
loop
htp.tablerowopen; -- genera la etiqueta <TR>
htp.tabledata (r_cus.id); -- genera las etiquetas <TD> y </TD>
htp.tabledata(r_cus.id);
htp.tabledata(r_cus.firstname);
htp.tabledata(r_cus.lastname);
htp.tabledata(r_cus.address);
htp.tabledata(r_cus.city);
htp.tabledata(r_cus.state);
htp.tabledata(r_cus.postalcode);
htp.tabledata(r_cus.email);
htp.tabledata(r_cus.phonenumber);
htp.tabledata(r_cus.creditlimit);
htp.tablerowclose; -- genera la etiqueta </TR>
end loop;
htp.tableclose; -- genera la etiqueta </TABLE>
end;

Al ejecutar la página podemos ver que hemos armado la tabla HTML desde dentro del código PL/SQL y cada registro se muestra en una fila diferente.

Asignamos estilos CSS dentro del código PL/SQL

Para darle una mejor visualización a nuestro informe en nuestra aplicación Apex, vamos a asignar algunos estilos en la tabla, fila y celdas.

Sintaxis Procedimiento TABLEOPEN

HTP.TABLEOPEN(   
cborder        IN       VARCHAR2   DEFAULT NULL   
calign         IN       VARCHAR2   DEFAULT NULL,   
cnowrap        IN       VARCHAR2   DEFAULT NULL,   
cclear         IN       VARCHAR2   DEFAULT NULL   
cattributes    IN       VARCHAR2   DEFAULT NULL);

 

Por ejemplo, este procedimiento genera:

<TABLE "cborder" NOWRAP ALIGN="calign" CLEAR="cclear" cattributes>

En nuestro código PL/SQL asignamos a la tabla los siguientes atributos:

    • Border: 2
    • Width: 100%
    • Cellpadding=10
    • Text=#000000 (negro)
    • bgcolor=#F3F3F3

 

htp.tableopen (cattributes => 'border=2 width=100% cellpadding=10 text=#000 bgcolor=#F3F3F3');

Reemplazamos el código PL/SQL en la región de contenido dinámico, por el siguiente código:

declare
       cursor c_cus
       is
       select apex_item.display_and_save(1, cus.customer_id) id
              , apex_item.display_and_save (2, cus.cust_first_name) firstname
              , apex_item.display_and_save (3, cus.cust_last_name) lastname
              , apex_item.display_and_save (4, cus.cust_street_address1) address
              , apex_item.display_and_save (5, cus.cust_city) city
              , apex_item.display_and_save (6, cus.cust_state) state
              , apex_item.display_and_save (7, cus.cust_postal_code) postalcode
              , apex_item.display_and_save (8, cus.cust_email) email
              , apex_item.display_and_save (9, phone_number1) phonenumber
              , apex_item.display_and_save (10, credit_limit) creditlimit   
       from demo_customers cus
       , demo_states sta
       where cus.cust_state = sta.st
       order by cus.customer_id;
begin
       htp.htmlopen; -- genera la etiqueta <HTML>
       htp.headopen; -- genera la etiqueta <THEAD>
       htp.title('Reporte de Clientes'); -- genera la etiqueta <TITLE> y </TITLE>
       htp.headclose; -- genera la etiqueta </HEAD>
       htp.bodyopen; -- genera la etiqueta <BODY>
       htp.header(1, 'Reporte de Clientes'); -- genera la etiqueta <H1> y </H1>
       htp.tableopen (cattributes => 'border=2 width=100% cellpadding=10 text=#000000 bgcolor=#F3F3F3'); -- genera la etiqueta <TABLE>
       htp.tablerowopen (cattributes => 'bgcolor="#216BB9"'); -- genera la etiqueta <TR>
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Cliente ID'); -- genera la etiqueta <TH> y </TH>
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Nombre');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Apellido');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Dirección');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Ciudad');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Estado');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Código Postal');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Email');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Teléfono');
       htp.tableheader(cvalue => '<font face="ARIAL" size="2.25" color="WHITE">' || 'Límite de Crédito');
       htp.tablerowclose; -- genera la etiqueta </TR>
       htp.tablerowopen; -- genera la etiqueta <TR>
       for r_cus in c_cus
       loop
       htp.tabledata(r_cus.id);
       htp.tabledata(r_cus.firstname,  cattributes=>'align=left');
       htp.tabledata(r_cus.lastname, cattributes=>'align=left');
       htp.tabledata(r_cus.address, cattributes=>'align=left');
       htp.tabledata(r_cus.city, cattributes=>'align=left');
       htp.tabledata(r_cus.state, cattributes=>'align=left');
       htp.tabledata(r_cus.postalcode, cattributes=>'align=center');
       htp.tabledata(r_cus.email, cattributes=>'align=left');
       htp.tabledata(r_cus.phonenumber, cattributes=>'align=left');
       htp.tabledata(r_cus.creditlimit, cattributes=>'align=center');
       htp.tablerowclose;
       end loop;
       htp.tablerowclose; -- genera la etiqueta </TR>
       htp.tableclose; -- genera la etiqueta </TABLE>
       htp.bodyclose; -- genera la etiqueta </BODY>
       htp.htmlclose; -- genera la etiqueta </HTML>
end;

Podemos ejecutar la página y visualizar los cambios en la región de Informe, mostrando la tabla con algunos estilos CSS.

Conclusión

En este artículo hemos podido ver cómo podemos crear Informes generados dinámicamente a partir de un código PL/SQL, además de aprender a utilizar los paquetes HTP y HTF para estructurar la información dentro de la región en la página de Apex y finalmente aprendimos a asignar estilos CSS desde PL/SQL.