Si bien Oracle Application Express nos ofrece muchos tipos de Informes para ser usados cuando estamos creando nuestras aplicaciones web, a veces no se ajusta a lo que necesitamos, y si tenemos un módulo de PL/SQL y necesitamos crear un informe visual, los asistentes de Informes de Apex en este caso no nos ayudan.
 
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)    APEX_ITEM.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)    APEX_ITEM.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 <HEAD>
        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.