<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[A little knowledge to share-Oracle APEX]]></title><description><![CDATA[Soy Ingeniero de Aplicaciones desde el 2012, he trabajado con Oracle APEX desde el 2017en versiones 5, 18, 20, 21. En los ultimos años he desarrollado habilidad]]></description><link>https://en.aflorestorres.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 23:29:43 GMT</lastBuildDate><atom:link href="https://en.aflorestorres.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Oracle APEX Invoice Generator: Simple Invoicing for Small Teams and Projects]]></title><description><![CDATA[Invoicing should not be complicated. You did the work, now you need a document with your details, the client's details, the services you provided, and a total. That is it.
The Oracle APEX Invoice Gene]]></description><link>https://en.aflorestorres.com/oracle-apex-invoice-generator-simple-invoicing-for-small-teams-and-projects</link><guid isPermaLink="true">https://en.aflorestorres.com/oracle-apex-invoice-generator-simple-invoicing-for-small-teams-and-projects</guid><category><![CDATA[#oracle-apex]]></category><category><![CDATA[apex.world]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Mon, 16 Mar 2026 01:36:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/63f9819af32e92faffdc220b/74a01156-c198-4ef6-9c75-5e521b97088d.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Invoicing should not be complicated. You did the work, now you need a document with your details, the client's details, the services you provided, and a total. That is it.</p>
<p>The <strong>Oracle APEX Invoice Generator</strong> is an open-source application designed to make that process as simple as possible. Register your business and your clients once, then creating an invoice is just a few clicks: pick the client, pick your business, add service lines, done. Preview it, print it, or save it as PDF straight from the browser.</p>
<p>It is built entirely in Oracle APEX — no external tools or extra licenses required. If you want to understand the printing technique behind it (HTML + CSS + <code>window.print()</code>), the companion post <a href="https://en.aflorestorres.com/zero-cost-print-in-oracle-apex-html-and-css-only">Zero-cost print in Oracle APEX: HTML and CSS only</a> covers that in detail. This post focuses on the <strong>application itself</strong> — the data model, the API, and the user experience.</p>
<hr />
<h2>How it works</h2>
<p>The idea is: set up your data once, then reuse it on every invoice.</p>
<p><strong>Lookup tables</strong> — you register your businesses (name, address, email, phone), your clients, and your bank/payment details. These are filled once and selected from dropdowns when you create an invoice.</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/6_invoice_generator_app/images/bank_report.png" alt="Bank Details report" style="display:block;margin:0 auto" />

<p><strong>Invoices</strong> — pick a client, a business, optionally a bank account, set the issue date, payment terms, tax rate, and any notes. Then add service lines — each with a description and an amount. The app calculates subtotal, tax, and total automatically.</p>
<p><strong>Print</strong> — open a modal preview and click Print. The browser handles the rest.</p>
<hr />
<h2>The page flow</h2>
<p>The app has five pages with a straightforward flow:</p>
<pre><code class="language-plaintext">Invoices list (100) → Create/Edit Invoice (110) → Add Line (115, modal)
                                                 → Print (120, modal)
</code></pre>
<p>The invoices list is your home base — all invoices in one Interactive Report:</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/6_invoice_generator_app/images/invoice_report.png" alt="Invoices Interactive Report" style="display:block;margin:0 auto" />

<p>Click any row to open the invoice form. The header fields and the service lines live on the same page, so you see everything at a glance:</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/6_invoice_generator_app/images/invoice_detail.png" alt="Invoice detail — form and lines" style="display:block;margin:0 auto" />

<p>From here you can add or edit lines (opens a modal), or hit the print icon to preview and print:</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/6_invoice_generator_app/images/print_invoice.png" alt="Print Invoice modal preview" style="display:block;margin:0 auto" />

<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/6_invoice_generator_app/images/print_2_save.png" alt="Browser print dialog — Save as PDF" style="display:block;margin:0 auto" />

<hr />
<h2>Database design</h2>
<p>Five tables, each with a clear responsibility:</p>
<pre><code class="language-plaintext">invg_businesses ──┐
                  ├──► invg_invoices ◄── invg_clients
invg_bank_details ┘          │
                             │
                    invg_invoice_lines
</code></pre>
<p><code>invg_businesses</code>, <code>invg_clients</code>, and <code>invg_bank_details</code> are lookup tables. <code>invg_invoices</code> is the invoice header (references the three lookups by FK, plus dates, terms, tax, notes). <code>invg_invoice_lines</code> holds the service lines.</p>
<p>All tables follow the same conventions: identity primary keys, audit columns populated via <a href="https://github.com/Aftorres02/OracleApex-Invoice-Generator/tree/main/triggers">compound triggers</a>, soft delete with <code>active_yn</code>, and <code>char</code> semantics on all <code>varchar2</code> columns.</p>
<hr />
<h2>The PL/SQL API</h2>
<p>All business logic lives in one package: <code>invg_invoice_api</code> (<a href="https://github.com/Aftorres02/OracleApex-Invoice-Generator/blob/main/packages/invg_invoice_api.pks">spec</a>). It handles soft delete, invoice number generation (<code>YYYY-NNN</code> format), and the HTML rendering.</p>
<p>The key function is <code>render_invoice_html</code>: it fetches the invoice with all its joins (client, business, bank), loops over the service lines, computes the totals, and returns a complete HTML fragment inside a <code>clob</code>. Sections like bank details and notes are conditionally included only when present. All user data is escaped with <code>apex_escape.html</code>.</p>
<p>Insert and update operations use APEX's native Automatic Row Processing — no custom DML needed.</p>
<hr />
<h2>Deployment</h2>
<p>The <a href="https://github.com/Aftorres02/OracleApex-Invoice-Generator">repository</a> includes a <code>release/_release.sql</code> script that runs everything in the correct order — views, packages, triggers, seed data, and the APEX application import:</p>
<pre><code class="language-bash">cd release
sqlcl user/pass@db @_release.sql
</code></pre>
<p>Log in to APEX and your invoicing app is ready.</p>
<hr />
<h2>Why use this</h2>
<ul>
<li><p><strong>Simple</strong> — the whole point is reducing friction. A few clicks to create an invoice, one click to print or save as PDF.</p>
</li>
<li><p><strong>Self-contained</strong> — runs entirely inside Oracle APEX. No external services, no integrations to maintain.</p>
</li>
<li><p><strong>Yours to extend</strong> — add a logo, change colors, add currency support, status workflows (Draft/Sent/Paid), email delivery via <code>apex_mail</code>. The codebase is small and readable.</p>
</li>
<li><p><strong>Open source</strong> — fork it, adapt it, make it yours.</p>
</li>
</ul>
<hr />
<h2>Get the code</h2>
<ul>
<li><strong>Full application</strong>: <a href="https://github.com/Aftorres02/OracleApex-Invoice-Generator">OracleApex-Invoice-Generator</a></li>
</ul>
<p>Clone, run the release script, and go send that invoice.</p>
<hr />
<p>#oracle-apex #apex #apexworld #apex-tips #oracle #orclapex #invoicing #plsql #open-source</p>
]]></content:encoded></item><item><title><![CDATA[Zero-cost print in Oracle APEX: HTML and CSS only]]></title><description><![CDATA[Many small applications need one or two simple print templates: an invoice, a receipt, or a short report. Buying extra reporting tools or PDF-generation licenses is often hard to justify for such a li]]></description><link>https://en.aflorestorres.com/zero-cost-print-in-oracle-apex-html-and-css-only</link><guid isPermaLink="true">https://en.aflorestorres.com/zero-cost-print-in-oracle-apex-html-and-css-only</guid><category><![CDATA[Oracle]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[#oracle-apex]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Thu, 12 Mar 2026 03:41:01 GMT</pubDate><content:encoded><![CDATA[<p>Many small applications need one or two simple print templates: an invoice, a receipt, or a short report. Buying extra reporting tools or PDF-generation licenses is often hard to justify for such a limited need. This post shows how you can get a page that prints (and "Save as PDF") using only HTML you generate, a PL/SQL Dynamic Content region, and CSS — no extra licenses and no libraries.</p>
<p><strong>This post is meant as inspiration:</strong> the steps and the example below are a pattern you can reuse for any print template you need. Use it as a starting point and adapt it to your own documents.</p>
<hr />
<h2>The three pieces of the solution</h2>
<h3>1. HTML</h3>
<p>Build the document <strong>server-side</strong> (e.g. in PL/SQL) as a single container — for example a <code>div</code> with a class like <code>invoice-doc</code> — and give it a clear structure: title, metadata, parties, table of lines, totals, notes. You can <strong>design or draft this HTML with AI</strong>: ask it to "generate a simple invoice HTML with From/To, a lines table, and totals," then adapt the markup and plug in your real data in PL/SQL. You keep full control and there is no need to be a designer.</p>
<h3>2. Dynamic region</h3>
<p>Use one region of type <strong>PL/SQL Dynamic Content</strong>. It calls a function (or block) that returns your HTML; the region outputs it so the page renders the HTML. The region has no title or uses a minimal template so the page is just your document plus a Print button.</p>
<h3>3. CSS</h3>
<p>Use one CSS block (or file) with two roles:</p>
<ul>
<li><p><strong>Layout</strong>: Styles for the document container (max-width, font, spacing, tables) so it looks good on screen.</p>
</li>
<li><p><strong>Print</strong>: A <code>@media print</code> block that hides everything on the page, then shows only your document container and its children. The browser print dialog then sees a clean page with no APEX chrome (header, nav, modal frame, buttons). Users can print or choose "Save as PDF" from the browser.</p>
</li>
</ul>
<hr />
<h2>Minimal example</h2>
<h3>PL/SQL: return a tiny document</h3>
<p>Build a small HTML fragment and return it in your Dynamic Content region so the page displays it as HTML:</p>
<pre><code class="language-sql">declare
  l_html clob;
begin
  l_html := '&lt;div class="invoice-doc"&gt;'
         || '&lt;h1&gt;Invoice&lt;/h1&gt;'
         || '&lt;p&gt;&lt;strong&gt;Number: 2026-001&lt;/strong&gt;&lt;/p&gt;'
         || '&lt;p&gt;Total: $100.00&lt;/p&gt;'
         || '&lt;/div&gt;';
  -- In the PL/SQL Dynamic Content region, output l_html so the page renders it as HTML.
end;
/
</code></pre>
<p>The complete PL/SQL example (function, region source, and full document structure) is in the repository: <a href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/5_easy_print_custom_html/invg_invoice_api.pks">invg_invoice_api.pks</a> and <a href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/5_easy_print_custom_html/invg_invoice_api.pkb">invg_invoice_api.pkb</a>.</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/5_easy_print_custom_html/images/add_render_html_fnc.png" alt="Add render HTML function in APEX — region source" style="display:block;margin:0 auto" />

<h3>CSS: layout + print</h3>
<p>The print CSS gives the document layout on screen and a <code>@media print</code> block that hides the rest of the page and shows only your document container, so the browser print dialog sees a clean page.</p>
<p><strong>Import into APEX:</strong> add the CSS to your application (e.g. upload as a <strong>Static Application File</strong> or paste into <strong>Page &gt; CSS &gt; Inline</strong>) and reference it on the page so it loads for the print preview.</p>
<p>Full file: <a href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/5_easy_print_custom_html/invg_invoice_print.css">invg_invoice_print.css</a>.</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/5_easy_print_custom_html/images/import_css_file.png" alt="Import CSS file in APEX" style="display:block;margin:0 auto" />

<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/5_easy_print_custom_html/images/load_css.png" alt="Load CSS on the page" style="display:block;margin:0 auto" />

<h3>JavaScript: trigger print</h3>
<p>Add a button (e.g. in the dialog footer) with a Dynamic Action that runs:</p>
<pre><code class="language-javascript">function invgPrintInvoice() {
  window.print();
}
</code></pre>
<p>Use a class like <code>no-print</code> on the button and hide it in <code>@media print</code> if you do not want it on the printed page.</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/5_easy_print_custom_html/images/print_btn_js.png" alt="Print button and JavaScript" style="display:block;margin:0 auto" />

<hr />
<h2>Full example and app</h2>
<p>All code for this post is in the repository: <a href="https://github.com/Aftorres02/blog_app_examples/tree/master/post_2026/5_easy_print_custom_html">5_easy_print_custom_html</a> (package spec and body, CSS, and <a href="https://github.com/Aftorres02/blog_app_examples/tree/master/post_2026/5_easy_print_custom_html/images">images</a>). The full application will be shared later in a separate post.</p>
<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/5_easy_print_custom_html/images/preview_invoice.png" alt="Preview in modal" style="display:block;margin:0 auto" />

<img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/5_easy_print_custom_html/images/print_invoice.png" alt="Print / Save as PDF" style="display:block;margin:0 auto" />

<hr />
<h2>Why this approach</h2>
<ul>
<li><p><strong>No extra licensing</strong> — You use only Oracle APEX and the browser. No reporting server or PDF library required.</p>
</li>
<li><p><strong>Save as PDF is built in</strong> — Users can choose "Save as PDF" or "Print to PDF" from the browser’s print dialog.</p>
</li>
<li><p><strong>You own the template</strong> — The HTML and CSS are yours; you can change the layout or reuse the pattern for any document (receipt, report, delivery note, quote).</p>
</li>
<li><p><strong>AI can help</strong> — You can use AI to draft the initial HTML structure (e.g. invoice layout, table markup), then wire it to your data in PL/SQL and adjust the styles as needed.</p>
</li>
</ul>
<hr />
<p>With a small amount of HTML, one dynamic region, and a focused print CSS, you can have print-ready pages at zero cost.</p>
<hr />
<p>#oracle-apex #apex #apexworld #apex-tips #oracle #orclapex #printing #pdf</p>
]]></content:encoded></item><item><title><![CDATA[Oracle APEX Hierarchy Visualizer - Template Component - Plugin]]></title><description><![CDATA[Hierarchical data is everywhere in enterprise applications—organization charts, file systems, category trees. While Oracle APEX provides native components like the Tree Region, sometimes we need more control over the look and feel within reports or c...]]></description><link>https://en.aflorestorres.com/oracle-apex-hierarchy-visualizer-template-component-plugin</link><guid isPermaLink="true">https://en.aflorestorres.com/oracle-apex-hierarchy-visualizer-template-component-plugin</guid><category><![CDATA[Oracle]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Apex Tips]]></category><category><![CDATA[Apex]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[CSS]]></category><category><![CDATA[Hierarchy]]></category><category><![CDATA[blog]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Sat, 07 Feb 2026 17:56:51 GMT</pubDate><content:encoded><![CDATA[<p>Hierarchical data is everywhere in enterprise applications—organization charts, file systems, category trees. While <strong>Oracle APEX</strong> provides native components like the Tree Region, sometimes we need more control over the look and feel within reports or custom layouts.</p>
<p>In this post, we'll explore the <strong>APEX Hierarchy Visualizer</strong>, a Template Component plugin designed to render nested data with elegant indentation and multiple visual styles.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/images/plugin_hierarchy.png?raw=true" alt="APEX Hierarchy Visualizer Preview" />
<em>(Transforming raw CONNECT BY data into a clean, visual hierarchy)</em></p>
<h2 id="heading-anatomy-of-the-apex-hierarchy-visualizer-plugin">Anatomy of the "APEX Hierarchy Visualizer" Plugin</h2>
<p>The power of this plugin lies in its ability to automatically handle indentation and node types (parent vs. leaf) based on standard SQL hierarchical queries.</p>
<h3 id="heading-1-dynamic-indentation-styles">1. Dynamic Indentation Styles</h3>
<p>The plugin isn't limited to just adding spaces. You can choose from several <code>Indent Style</code> options to match your UI requirements:</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/images/p_h_config_styles.png?raw=true" alt="Plugin Configuration Styles" /></p>
<ul>
<li><strong>Folder/File Icons</strong>: The classic view using Font Awesome icons (orange folders for parents, gray files for leaves).</li>
<li><strong>Chevrons</strong>: A modern, minimalist look with directional arrows indicating depth.</li>
<li><strong>Solid &amp; Dotted Lines</strong>: Provides a "stretched" visual connector from the start of the row to the label.</li>
<li><strong>L-Steps (└─)</strong>: A clean ASCII-style branching visualization.</li>
<li><strong>Spaces Only</strong>: For when you just need the structural alignment without icons.</li>
</ul>
<h3 id="heading-2-custom-attributes-for-full-control">2. Custom Attributes for Full Control</h3>
<p>Configurable directly in the Page Designer, these attributes allow you to tailor the visualization:</p>
<ul>
<li><strong>Hierarchy Level Column</strong>: Maps to the <code>LEVEL</code> pseudo-column from your query.</li>
<li><strong>Is Leaf Column</strong>: Maps to <code>CONNECT_BY_ISLEAF</code> to distinguish between folders and items.</li>
<li><strong>Indentation (px)</strong>: Precisely control how many pixels each level should shift (default is 20px).</li>
<li><strong>Node Colors</strong>: Override default colors for Parent and Leaf nodes using the integrated Color Picker.</li>
</ul>
<p><img src="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/images/plugin_hierarchy_sql.png?raw=true" alt="SQL" /></p>
<h2 id="heading-how-to-use">How to Use</h2>
<p>Setting up the hierarchy visualizer in your application is a straightforward process:</p>
<ol>
<li><p><strong>Prepare Your Data</strong>: Use a <code>CONNECT BY</code> or <code>Recursive CTE</code> query to generate the hierarchy, ensuring you include <code>LEVEL</code> and <code>CONNECT_BY_ISLEAF</code>.
_Ref: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/demo_hierarchical_data_table.sql">demo_hierarchical_data_table.sql</a>_</p>
</li>
<li><p><strong>Add the Plugin</strong>: Import the plugin SQL file and the corresponding CSS style.</p>
<ul>
<li><a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/plugin/hierarchy_template_component.css">Download CSS</a></li>
<li><a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/plugin/template_component_plugin_hierarchy_visualizer.sql">Download Plugin SQL</a></li>
</ul>
</li>
<li><p><strong>Assign the Type</strong>: Change your report column type to <strong>APEX Hierarchy Visualizer</strong>.</p>
</li>
<li><p><strong>Map Attributes</strong>: In the column attributes, select the corresponding source columns for Level, Is Leaf, and the Label you want to display.</p>
</li>
<li><p><strong>Fine-tune</strong>: Choose your preferred Indent Style and adjust the Indent Step to fit your design.</p>
</li>
</ol>
<p><img src="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/images/config_plugin_hierarchy.png?raw=true" alt="Plugin Configuration Options" />
<em>(Example of plugin configuration in Page Designer)</em></p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><strong>Full Repository</strong>: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/tree/master/post_2026/4_plugin_hierarchy">Access the source code here</a></li>
<li><strong>Plugin Source</strong>: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/tree/master/post_2026/4_plugin_hierarchy/plugin">Plugin Folder</a></li>
<li><strong>Manual Config Guide</strong>: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/plugin_manual_config.md">Read the implementation details</a></li>
<li><strong>SQL Example</strong>: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/4_plugin_hierarchy/sql.sql">Sample hierarchical query</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[APEX HEX Color Visualizer - Oracle APEX Plugin]]></title><description><![CDATA[One of the most powerful and often underutilized features of Oracle APEX is Template Components. These allow us to encapsulate complex UI logic into reusable and easy-to-configure components.
In this post, we'll see how to "display colors" using a pl...]]></description><link>https://en.aflorestorres.com/apex-hex-color-visualizer-oracle-apex-plugin</link><guid isPermaLink="true">https://en.aflorestorres.com/apex-hex-color-visualizer-oracle-apex-plugin</guid><category><![CDATA[Oracle]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Apex Tips]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[Oracle Database]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[CSS]]></category><category><![CDATA[plugins]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Tue, 03 Feb 2026 02:36:39 GMT</pubDate><content:encoded><![CDATA[<p>One of the most powerful and often underutilized features of Oracle APEX is <strong>Template Components</strong>. These allow us to encapsulate complex UI logic into reusable and easy-to-configure components.</p>
<p>In this post, we'll see how to "display colors" using a plugin. The goal is to go from showing a simple hex code (e.g., <code>#FF4436</code>) to a rich visual component with multiple customization options—dot, radio, square, badge, pill, etc.—usable in Interactive Reports.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2026/2_apex_color_visualizer/basic_hex_example.png" alt="APEX Color Visualizer Preview" />
<em>(This is the final result: a single plugin, multiple visual representations)</em></p>
<h2 id="heading-anatomy-of-our-apex-color-visualizer-plugin">Anatomy of Our "APEX Color Visualizer" Plugin</h2>
<p>For this example, we have designed a plugin that supports a large number of visual variations controlled by custom attributes.</p>
<h3 id="heading-1-multiple-display-styles">1. Multiple Display Styles</h3>
<p>The core of the plugin allows you to choose how to render the data using the <code>STYLE</code> attribute:</p>
<ul>
<li><strong>Pill</strong>: Ideal for prominent statuses.</li>
<li><strong>Badge</strong>: Subtle, light background with colored border.</li>
<li><strong>Radio / Dot</strong>: Minimalist indicators alongside the text.</li>
<li><strong>Square</strong>: Palette-style color swatches.</li>
</ul>
<h3 id="heading-2-custom-attributes">2. Custom Attributes</h3>
<p>What makes this plugin truly useful is its flexibility. We have defined attributes that can be configured directly from the Page Designer:</p>
<ul>
<li><strong>Size</strong>: Full size control (Extra Small, Small, Medium, Large, Extra Large).</li>
<li><strong>Show Text</strong>: Option to hide the Hex code and leave only the visual indicator.</li>
<li><strong>Shadow &amp; Border</strong>: Boolean attributes to add depth or defined borders.</li>
<li><strong>Position</strong>: Flexible alignment of the indicator relative to the text (left, right, top, bottom).</li>
</ul>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2026/2_apex_color_visualizer/hex_plugin%20options.png" alt="Plugin Options" /></p>
<p>You can view the full styles here: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/2_apex_color_visualizer/apex_color_visualizer.css">apex_color_visualizer.css</a></p>
<h2 id="heading-how-to-use">How to Use</h2>
<p>Implementing it in your applications is simple, follow these steps:</p>
<ol>
<li><strong>Create Your Report</strong>: Create an Interactive Report or Classic Report that contains a column with hex color codes.</li>
<li><strong>Select the Column</strong>: In Page Designer, select the color column.</li>
<li><strong>Change the Type</strong>: Change the column type to <strong>APEX Color Visualizer</strong>.</li>
<li><strong>Configure</strong>: Adjust the attributes (Style, Size, Shadow, etc.) according to your needs.</li>
</ol>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2026/2_apex_color_visualizer/hex%20color%20plugin.png" alt="Plugin Configuration" />
<em>(Plugin configuration panel in Page Designer)</em></p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><strong>Full Repository</strong>: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/tree/master/post_2026/2_apex_color_visualizer">Access the source code here</a></li>
<li><strong>Plugin Installation</strong>: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/2_apex_color_visualizer/template_component_plugin_apex_color_visualizer.sql">template_component_plugin_apex_color_visualizer.sql</a></li>
<li><strong>CSS Styles</strong>: <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/2_apex_color_visualizer/apex_color_visualizer.css">apex_color_visualizer.css</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Y/N Template Component Plugin for Oracle APEX]]></title><description><![CDATA[Oracle APEX Interactive Reports often display boolean or flag columns (like active_yn, enabled_yn) with plain text values "Y" or "N". While functional, this approach lacks visual clarity and modern UX standards.
In this post, we'll explore how to cre...]]></description><link>https://en.aflorestorres.com/yn-template-component-plugin-for-oracle-apex</link><guid isPermaLink="true">https://en.aflorestorres.com/yn-template-component-plugin-for-oracle-apex</guid><category><![CDATA[Oracle]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Apex Tips]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Sun, 18 Jan 2026 04:56:36 GMT</pubDate><content:encoded><![CDATA[<p>Oracle APEX Interactive Reports often display boolean or flag columns (like <code>active_yn</code>, <code>enabled_yn</code>) with plain text values "Y" or "N". While functional, this approach lacks visual clarity and modern UX standards.</p>
<p>In this post, we'll explore how to create a <strong>custom template component plugin</strong> that automatically displays <strong>green checkmarks (✓)</strong> for "Y" values and <strong>red crosses (✗)</strong> for "N" values in your APEX reports, providing instant visual feedback to users.</p>
<p><a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/tree/master/post_2026/1_plugin_y_n">Complete Implementation on GitHub</a> - Full working code examples</p>
<h2 id="heading-what-is-a-template-component-plugin">What is a Template Component Plugin?</h2>
<p>A Template Component Plugin in Oracle APEX is a reusable UI component that can be applied to report columns, regions, or other UI elements. It uses APEX's declarative template syntax to transform data values into custom HTML output, allowing developers to create consistent, branded user interfaces without writing complex JavaScript.</p>
<p>This implementation creates a <strong>template component plugin</strong> that transforms Y/N values into intuitive visual indicators:</p>
<ul>
<li>✅ <strong>Green checkmark (✓)</strong> for "Y" values - indicating active, enabled, or positive states</li>
<li>❌ <strong>Red cross (✗)</strong> for "N" values - indicating inactive, disabled, or negative states</li>
</ul>
<h3 id="heading-visual-result">Visual Result</h3>
<p>The plugin displays values as shown in the following table:</p>
<p><img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/1_plugin_y_n/images/report_result.png" alt="Report Result with Y/N Icons" /></p>
<h2 id="heading-installation-instructions">Installation Instructions</h2>
<h3 id="heading-step-1-install-the-css-file">Step 1: Install the CSS File</h3>
<p>First, upload the <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/1_plugin_y_n/y_n.css"><code>y_n.css</code></a> file as a workspace static file at <code>css/y_n.css</code>. The file contains the following styles:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.icon-check</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#10b981</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
}
<span class="hljs-selector-class">.icon-x</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#ef4444</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
}
</code></pre>
<h3 id="heading-step-2-install-the-template-component-plugin">Step 2: Install the Template Component Plugin</h3>
<p>Install the template component plugin by running the <a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/1_plugin_y_n/template_component_plugin_y_n.sql"><code>template_component_plugin_y_n.sql</code></a> script:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Run the plugin installation script</span>
@template_component_plugin_y_n.sql
</code></pre>
<p>This creates a plugin named <strong>Y_N</strong> that can be used as a column template in Interactive Reports.</p>
<h2 id="heading-usage-in-interactive-reports">Usage in Interactive Reports</h2>
<h3 id="heading-applying-the-plugin-to-a-column">Applying the Plugin to a Column</h3>
<ol>
<li>Navigate to your Interactive Report in Page Designer</li>
<li>Select the column you want to format (e.g., <code>active_yn</code>)</li>
<li>In the column properties, go to <strong>Column Formatting</strong> → <strong>Template</strong></li>
<li>Select <strong>Y_N</strong> from the Template Component dropdown</li>
<li>In the <strong>Value</strong> attribute, select the column containing your Y/N value (typically the same column)</li>
</ol>
<h3 id="heading-example">Example</h3>
<p><img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2026/1_plugin_y_n/images/implementation.png" alt="Applying Y_N Template Component" /></p>
<p>After applying the Y_N template component to the <code>active_yn</code> column, the values will automatically display as green checkmarks (✓) for "Y" and red crosses (✗) for "N".</p>
<h2 id="heading-implementation-details">Implementation Details</h2>
<h3 id="heading-template-component-structure">Template Component Structure</h3>
<p>The plugin uses APEX's declarative template syntax to conditionally render icons based on the column value: template_component_plugin_y_n.sql</p>
<pre><code class="lang-html">{if APEX$IS_LAZY_LOADING/}
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>#VALUE#<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

{else/}

  {if ?VALUE/}

      {case VALUE/}
        {when Y/} <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"icon-check"</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"#VALUE#"</span>&gt;</span>✓<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

        {when N/} <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"icon-x"</span>&gt;</span>✗<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        {otherwise/}
      {endcase/}

  {endif/}


{endif/}
</code></pre>
<h3 id="heading-css-styling">CSS Styling</h3>
<p>The CSS file provides color coding for immediate visual feedback:</p>
<ul>
<li><strong>Green (#10b981)</strong>: Tailwind's <code>emerald-500</code> color for positive/active states</li>
<li><strong>Red (#ef4444)</strong>: Tailwind's <code>red-500</code> color for negative/inactive states</li>
<li><strong>Font Size (0.9rem)</strong>: Slightly smaller than default text for better proportion in tables</li>
</ul>
<h2 id="heading-customization-options">Customization Options</h2>
<h3 id="heading-changing-colors">Changing Colors</h3>
<p>To modify the colors, edit the <code>css/y_n.css</code> file:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.icon-check</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#22c55e</span>;  <span class="hljs-comment">/* Change to your preferred green */</span>
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
}
<span class="hljs-selector-class">.icon-x</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#dc2626</span>;  <span class="hljs-comment">/* Change to your preferred red */</span>
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.9rem</span>;
}
</code></pre>
<hr />
<p><strong>Files in this post:</strong></p>
<ul>
<li><a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/1_plugin_y_n/template_component_plugin_y_n.sql"><code>template_component_plugin_y_n.sql</code></a> - Plugin installation script</li>
<li><a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2026/1_plugin_y_n/y_n.css"><code>y_n.css</code></a> - CSS styling file</li>
<li><code>plugin_y_n_en.md</code> - This blog post (English)</li>
<li><code>plugin_y_n_es.md</code> - Blog post (Spanish)</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[# Oracle APEX JS Logger: Control Your JavaScript Logs to Debug Faster and Safer]]></title><description><![CDATA[Oracle APEX JS Logger was born from the need to have JavaScript logging with two important functionalities: controlling the amount of logs displayed in console, depending on the case and the debugging being done. And the other important functionality...]]></description><link>https://en.aflorestorres.com/oracle-apex-js-logger-control-your-javascript-logs-to-debug-faster-and-safer</link><guid isPermaLink="true">https://en.aflorestorres.com/oracle-apex-js-logger-control-your-javascript-logs-to-debug-faster-and-safer</guid><category><![CDATA[Oracle]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[ logger]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Mon, 03 Nov 2025 02:52:37 GMT</pubDate><content:encoded><![CDATA[<p>Oracle APEX JS Logger was born from the need to have JavaScript logging with two important functionalities: controlling the amount of logs displayed in console, depending on the case and the debugging being done. And the other important functionality is having logging at the production level, where errors are not reported directly and that makes debugging difficult.</p>
<ul>
<li><strong><a target="_blank" href="https://github.com/Aftorres02/OracleAPEX_JS_Logger">GitHub</a>:</strong> Full source code and documentation</li>
</ul>
<h2 id="heading-the-problem-consolelog">The Problem: console.log</h2>
<p>When working with JavaScript, we usually use console.log for debugging. However, it can generate so much content in the console that it becomes difficult to manage, and practically impossible to use in production.</p>
<ul>
<li>A user reports an issue, but you can't reproduce it</li>
<li>You see the error in DevTools, but users in production don't have it open</li>
<li>No audit trail for user actions</li>
<li>No performance metrics for slow operations</li>
<li>Sensitive data might accidentally get logged</li>
</ul>
<h2 id="heading-js-logger-main-features">JS-Logger, Main Features</h2>
<ul>
<li><strong>Color</strong> configuration for console</li>
<li><strong>logger.logServer</strong>, to log to the database using PL/SQL Logger</li>
<li>Automatic data masking for sensitive fields</li>
<li><strong>Performance timing</strong> to find bottlenecks</li>
<li><strong>Modules</strong> to know where the console.log was launched from</li>
<li><strong>Configuration according to environment</strong> DEV, UAT, PROD</li>
</ul>
<h2 id="heading-how-to-get-started">How to Get Started</h2>
<h3 id="heading-step-1-load-the-files">Step 1: Load the Files</h3>
<p>Load the files into your APEX application:</p>
<pre><code class="lang-html">#WORKSPACE_FILES#js/logger-config.js
#WORKSPACE_FILES#js/logger.js
</code></pre>
<h3 id="heading-step-2-usage">Step 2: Usage</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Basic usage</span>
namespace.logger.log(<span class="hljs-string">'Application started'</span>, <span class="hljs-string">'AppMain'</span>);

<span class="hljs-comment">// With data</span>
namespace.logger.log(<span class="hljs-string">'User action'</span>, <span class="hljs-string">'UI'</span>, {
  <span class="hljs-attr">action</span>: <span class="hljs-string">'button_click'</span>,
  <span class="hljs-attr">pageId</span>: apex.env.APP_PAGE_ID
});

<span class="hljs-comment">// For production events</span>
namespace.logger.logServer(<span class="hljs-string">'Payment processed'</span>, <span class="hljs-string">'Payments'</span>, {
  <span class="hljs-attr">orderId</span>: <span class="hljs-string">'ORD-123'</span>,
  <span class="hljs-attr">amount</span>: <span class="hljs-number">99.99</span>
});
</code></pre>
<h2 id="heading-advanced-examples">Advanced Examples</h2>
<h3 id="heading-1-three-log-levels-with-colors">1. Three Log Levels with Colors</h3>
<p>The logger provides three log levels that automatically output in different colors:</p>
<p><strong>Information (Blue)</strong></p>
<pre><code class="lang-javascript">namespace.logger.log(<span class="hljs-string">'User logged in'</span>, <span class="hljs-string">'Auth'</span>, { <span class="hljs-attr">userId</span>: <span class="hljs-number">123</span> });
</code></pre>
<p><strong>Warning (Orange)</strong></p>
<pre><code class="lang-javascript">namespace.logger.warning(<span class="hljs-string">'API slow'</span>, <span class="hljs-string">'Network'</span>, { <span class="hljs-attr">responseTime</span>: <span class="hljs-number">3500</span> });
</code></pre>
<p><strong>Error (Red)</strong></p>
<pre><code class="lang-javascript">namespace.logger.error(<span class="hljs-string">'Validation failed'</span>, <span class="hljs-string">'Form'</span>, { <span class="hljs-attr">field</span>: <span class="hljs-string">'email'</span> });
</code></pre>
<h3 id="heading-2-module-scoped-loggers">2. Module-Scoped Loggers</h3>
<p>Create a logger for your module with persistent context. This is useful when building other JS libraries and you want to integrate it:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Create a module logger</span>
<span class="hljs-keyword">var</span> logger = namespace.logger.createModuleLogger(<span class="hljs-string">'PaymentModule'</span>);

<span class="hljs-comment">// Set context that applies to all logs from this module</span>
logger.setExtra({ <span class="hljs-attr">version</span>: <span class="hljs-string">'2.0'</span>, <span class="hljs-attr">feature</span>: <span class="hljs-string">'checkout'</span> });

<span class="hljs-comment">// Now all logs automatically include the context</span>
logger.log(<span class="hljs-string">'Processing payment'</span>, { <span class="hljs-attr">amount</span>: <span class="hljs-number">99.99</span> });
logger.error(<span class="hljs-string">'Payment failed'</span>, { <span class="hljs-attr">reason</span>: <span class="hljs-string">'insufficient_funds'</span> });

<span class="hljs-comment">// Clear context when done</span>
logger.clearExtra();
</code></pre>
<h3 id="heading-3-console-vs-server-logging">3. Console vs Server Logging</h3>
<p>Three logging modes for different needs:</p>
<p><strong>Console-only</strong> (development)</p>
<pre><code class="lang-javascript">namespace.logger.log(<span class="hljs-string">'Debug info'</span>, <span class="hljs-string">'MyModule'</span>, { <span class="hljs-attr">data</span>: <span class="hljs-string">'value'</span> });
</code></pre>
<p><strong>Console + Database</strong> (production)</p>
<pre><code class="lang-javascript">namespace.logger.logServer(<span class="hljs-string">'Important event'</span>, <span class="hljs-string">'Business'</span>, { 
  <span class="hljs-attr">orderId</span>: <span class="hljs-string">'ORD-123'</span>,
  <span class="hljs-attr">action</span>: <span class="hljs-string">'order_shipped'</span> 
});
</code></pre>
<p><strong>Console + Database with options</strong> (manual control per log)</p>
<pre><code class="lang-javascript">namespace.logger.log(<span class="hljs-string">'User completed checkout'</span>, <span class="hljs-string">'Ecommerce'</span>, {
  <span class="hljs-attr">orderId</span>: <span class="hljs-string">'ORD-123'</span>,
  <span class="hljs-attr">total</span>: <span class="hljs-number">99.99</span>
}, {
  <span class="hljs-attr">sendToServer</span>: <span class="hljs-literal">true</span>  <span class="hljs-comment">// Enable server sending</span>
});
</code></pre>
<h3 id="heading-4-automatic-data-masking">4. Automatic Data Masking</h3>
<p>Security is built-in. Sensitive fields are automatically masked:</p>
<pre><code class="lang-javascript">namespace.logger.log(<span class="hljs-string">'Login attempt'</span>, <span class="hljs-string">'Auth'</span>, {
  <span class="hljs-attr">username</span>: <span class="hljs-string">'john.doe'</span>,
  <span class="hljs-attr">password</span>: <span class="hljs-string">'secret123'</span>,  <span class="hljs-comment">// Automatically becomes: ***MASKED***</span>
  <span class="hljs-attr">token</span>: <span class="hljs-string">'abc123'</span>         <span class="hljs-comment">// Automatically becomes: ***MASKED***</span>
});
</code></pre>
<h3 id="heading-5-performance-timing">5. Performance Timing</h3>
<p>Measure how long operations take:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Start timing</span>
namespace.logger.timeStart(<span class="hljs-string">'page-load'</span>);

<span class="hljs-comment">// ... do work ...</span>

<span class="hljs-comment">// Stop and log elapsed time</span>
<span class="hljs-keyword">var</span> elapsed = namespace.logger.timeStop(<span class="hljs-string">'page-load'</span>, <span class="hljs-string">'Performance'</span>);
<span class="hljs-comment">// Output: "page-load completed in 125.43ms"</span>
</code></pre>
<h2 id="heading-configuration-and-customization">Configuration and Customization</h2>
<h3 id="heading-turning-off-and-customizing-output">Turning Off and Customizing Output</h3>
<p>To turn off logging completely we use LEVEL set to OFF.
Then depending on what we need we can use:</p>
<ul>
<li><strong>INFORMATION</strong>: shows everything</li>
<li><strong>WARNING</strong>: shows warnings and errors</li>
<li><strong>ERROR</strong>: only errors</li>
<li><strong>OFF</strong>: turns off all console logs</li>
</ul>
<p>To return to the initial configuration use <code>resetLevel()</code> or set <code>level: 'INFORMATION'</code>.</p>
<pre><code class="lang-javascript">namespace.loggerConfig.configure({
  <span class="hljs-attr">level</span>: <span class="hljs-string">'OFF'</span>,  <span class="hljs-comment">// OFF | ERROR | WARNING | INFORMATION</span>
});

<span class="hljs-comment">// Reset the level according to the main configuration</span>
namespace.loggerConfig.resetLevel();


<span class="hljs-comment">// Development: detailed console output</span>
<span class="hljs-keyword">var</span> devConfig = namespace.loggerConfig.getEnvConfig(<span class="hljs-string">'development'</span>);
namespace.loggerConfig.configure(devConfig);

<span class="hljs-comment">// Production: errors only, with server logging</span>
<span class="hljs-keyword">var</span> prodConfig = namespace.loggerConfig.getEnvConfig(<span class="hljs-string">'production'</span>);
namespace.loggerConfig.configure(prodConfig);
</code></pre>
<h3 id="heading-other-configuration-values">Other Configuration Values</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> DEFAULT_CONFIG = {
  <span class="hljs-comment">// Logging behavior</span>
  <span class="hljs-attr">level</span>:                  <span class="hljs-string">'INFORMATION'</span>,        <span class="hljs-comment">// Console log level - values: OFF, ERROR, WARNING, INFORMATION</span>
  <span class="hljs-attr">enableServer</span>:           <span class="hljs-literal">true</span>,                 <span class="hljs-comment">// Enable server logging (database storage)</span>

  <span class="hljs-comment">// Server logging configuration</span>
  <span class="hljs-attr">serverProcessName</span>:     <span class="hljs-string">'JS_LOGGER'</span>,          <span class="hljs-comment">// APEX process name for server logging</span>
  <span class="hljs-attr">retryCount</span>:             <span class="hljs-number">1</span>,                    <span class="hljs-comment">// Maximum number of retry attempts</span>
  <span class="hljs-attr">retryAttemptInitial</span>:    <span class="hljs-number">0</span>,                    <span class="hljs-comment">// Initial retry attempt counter</span>
  <span class="hljs-attr">retryDelayBase</span>:         <span class="hljs-number">1000</span>,                 <span class="hljs-comment">// Base delay in milliseconds</span>

  <span class="hljs-comment">// Default values</span>
  <span class="hljs-attr">defaultModuleName</span>:      <span class="hljs-string">'JS_LOGGER'</span>,          <span class="hljs-comment">// Default module name</span>
  <span class="hljs-attr">defaultUserName</span>:        <span class="hljs-string">'UNKNOWN'</span>,            <span class="hljs-comment">// Default user name</span>

  <span class="hljs-comment">// Security and data handling</span>
  <span class="hljs-attr">enableDataMasking</span>:      <span class="hljs-literal">true</span>,                 <span class="hljs-comment">// Enable masking of sensitive fields</span>
  <span class="hljs-attr">sensitiveFields</span>:        [<span class="hljs-string">'password'</span>, <span class="hljs-string">'token'</span>, <span class="hljs-string">'ssn'</span>],  <span class="hljs-comment">// Fields to mask</span>
  <span class="hljs-attr">maxDataSize</span>:            <span class="hljs-number">10000</span>,                <span class="hljs-comment">// Maximum data size in bytes</span>
  <span class="hljs-attr">maxErrorStringLength</span>:    <span class="hljs-number">100</span>,                  <span class="hljs-comment">// Maximum error string length</span>

  <span class="hljs-comment">// Timing configuration</span>
  <span class="hljs-attr">timingDecimalPlaces</span>:    <span class="hljs-number">2</span>                     <span class="hljs-comment">// Decimal places for timing</span>
};
</code></pre>
<h2 id="heading-learn-more">Learn More</h2>
<ul>
<li><strong><a target="_blank" href="https://github.com/Aftorres02/OracleAPEX_JS_Logger/tree/main/examples">Examples</a>:</strong> 5 focused examples covering all features</li>
<li><strong><a target="_blank" href="https://github.com/Aftorres02/OracleAPEX_JS_Logger/tree/main/demo">Demo</a>:</strong> Complete payment module implementation</li>
<li><strong><a target="_blank" href="https://github.com/Aftorres02/OracleAPEX_JS_Logger">GitHub</a>:</strong> Full source code and documentation</li>
<li><strong><a target="_blank" href="https://youtu.be/ktqXfO4LaTY">Video</a></strong>
Video with short explanatión.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[A Simple Oracle Advanced Queue (DBMS AQ) Example: Automatic Callback #JoelKallmanDay]]></title><description><![CDATA[Oracle Advanced Queuing (AQ) provides a powerful mechanism for automatic, event-driven data processing with built-in retry mechanisms and callback functions.
In this post, we'll explore how to implement a fully automated data synchronization system u...]]></description><link>https://en.aflorestorres.com/a-simple-oracle-advanced-queue-dbms-aq-example-automatic-callback-joelkallmanday</link><guid isPermaLink="true">https://en.aflorestorres.com/a-simple-oracle-advanced-queue-dbms-aq-example-automatic-callback-joelkallmanday</guid><category><![CDATA[Oracle Advanced Queuing]]></category><category><![CDATA[DBMS AQ]]></category><category><![CDATA[advanced queuing]]></category><category><![CDATA[Oracle Database]]></category><category><![CDATA[PL/SQL]]></category><category><![CDATA[message queue]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Thu, 16 Oct 2025 03:17:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760583952426/e87282a7-d4b2-486e-ad07-c48a014ad40b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Oracle Advanced Queuing (AQ) provides a powerful mechanism for <strong>automatic, event-driven data processing</strong> with built-in retry mechanisms and callback functions.</p>
<p>In this post, we'll explore how to implement a <strong>fully automated data synchronization system</strong> using DBMS_AQ callbacks that automatically process messages as they arrive, with intelligent retry handling for failed operations.</p>
<p><a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2025/20_DBMS_AQ/simple_queue_example.sql">Complete Implementation on GitHub</a> - Full working code example</p>
<h2 id="heading-what-is-oracle-advanced-queuing">What is Oracle Advanced Queuing?</h2>
<p>Oracle Advanced Queuing is a message queuing system built into the Oracle Database that enables <strong>automatic, event-driven communication</strong> between applications. Key features include:</p>
<ul>
<li><strong>Automatic message processing</strong> via callback functions</li>
<li><strong>Built-in retry mechanisms</strong> for failed operations</li>
<li><strong>Event-driven architectures</strong> with immediate processing</li>
<li><strong>Reliable message delivery</strong> with persistence and recovery</li>
</ul>
<h2 id="heading-the-problem-system-integration">The Problem: System Integration</h2>
<p>Modern enterprises often need to synchronize data between multiple systems:</p>
<ul>
<li>CRM systems to data warehouses</li>
<li>ERP systems to analytics databases</li>
<li>Web applications to backup systems</li>
<li>Real-time data feeds between microservices</li>
</ul>
<h2 id="heading-the-solution-aq-based-data-synchronization">The Solution: AQ-Based Data Synchronization</h2>
<p>Our implementation creates a <strong>fully automated queue-based system</strong> that handles synchronization tasks asynchronously with automatic retry capabilities and <strong>immediate callback processing</strong>.</p>
<h3 id="heading-key-improvements-over-basic-aq-tutorial">Key Improvements Over Basic AQ Tutorial</h3>
<p>This implementation extends the <a target="_blank" href="https://oracle-base.com/articles/9i/advanced-queuing-9i">Oracle-Base AQ tutorial</a> with:</p>
<ul>
<li>✅ <strong>Automatic callback processing</strong> - Messages processed immediately upon arrival</li>
<li>✅ <strong>Built-in retry mechanisms</strong> - Failed operations automatically retry with configurable delays</li>
<li>✅ <strong>Comprehensive monitoring</strong> - Track retry counts, message states, and operation history</li>
<li>✅ <strong>Production-ready features</strong> - Error handling, audit trails, and status tracking</li>
</ul>
<h3 id="heading-architecture-overview">Architecture Overview</h3>
<pre><code>┌─────────────────┐    ┌──────────────┐    ┌──────────────┐    ┌─────────────────┐
│   Source        │───▶│  AQ Queue    │───▶│ Auto Callback│───▶│   Target        │
│   System        │    │ (<span class="hljs-keyword">with</span> Retry) │    │ (Immediate)  │    │   System        │
└─────────────────┘    └──────────────┘    └──────────────┘    └─────────────────┘
                              │                      │
                              ▼                      ▼
                       ┌──────────────┐
                       │ Retry Logic  │
                       │ (<span class="hljs-number">5</span> attempts) │
                       └──────────────┘
</code></pre><h2 id="heading-implementation-details">Implementation Details</h2>
<h3 id="heading-1-database-schema-setup">1. Database Schema Setup</h3>
<p>First, we create a dedicated schema for our AQ implementation:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create user with necessary privileges</span>
<span class="hljs-keyword">create</span> <span class="hljs-keyword">user</span> <span class="hljs-string">"AQ_DEMO"</span> <span class="hljs-keyword">identified</span> <span class="hljs-keyword">by</span> <span class="hljs-string">"Password123*"</span>;
<span class="hljs-keyword">grant</span> <span class="hljs-keyword">create</span> <span class="hljs-keyword">session</span>, <span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span>, <span class="hljs-keyword">create</span> <span class="hljs-keyword">type</span>, <span class="hljs-keyword">create</span> <span class="hljs-keyword">procedure</span> <span class="hljs-keyword">to</span> <span class="hljs-string">"AQ_DEMO"</span>;
<span class="hljs-keyword">grant</span> <span class="hljs-keyword">execute</span> <span class="hljs-keyword">on</span> dbms_aqadm, dbms_aq <span class="hljs-keyword">to</span> <span class="hljs-string">"AQ_DEMO"</span>;
<span class="hljs-keyword">grant</span> <span class="hljs-keyword">unlimited</span> <span class="hljs-keyword">tablespace</span> <span class="hljs-keyword">to</span> <span class="hljs-string">"AQ_DEMO"</span>;
</code></pre>
<h3 id="heading-2-sync-operations-tracking">2. Sync Operations Tracking</h3>
<p>We maintain a history of all synchronization operations:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> sync_operations (
    <span class="hljs-keyword">id</span>                <span class="hljs-built_in">number</span> <span class="hljs-keyword">generated</span> <span class="hljs-keyword">by</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> <span class="hljs-keyword">identity</span> primary <span class="hljs-keyword">key</span>,
    source_system     <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
    target_system     <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
    sync_type         <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">30</span>) <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
    record_count      <span class="hljs-built_in">number</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>,
    sync_status       <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">20</span>) <span class="hljs-keyword">default</span> <span class="hljs-string">'COMPLETED'</span>,
    created_on        <span class="hljs-built_in">timestamp</span> <span class="hljs-keyword">with</span> <span class="hljs-keyword">local</span> <span class="hljs-built_in">time</span> zone <span class="hljs-keyword">default</span> <span class="hljs-keyword">localtimestamp</span>
);
</code></pre>
<p><strong>Key Features:</strong></p>
<ul>
<li><strong>Identity column</strong> for automatic ID generation</li>
<li><strong>Audit trail</strong> with creation timestamps</li>
<li><strong>Status tracking</strong> (PENDING, PROCESSING, COMPLETED, FAILED)</li>
<li><strong>Sync type classification</strong> (FULL, INCREMENTAL, DELTA, REALTIME)</li>
</ul>
<h3 id="heading-3-message-type-definition">3. Message Type Definition</h3>
<p>We define a custom object type for our sync messages:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">replace</span> <span class="hljs-keyword">type</span> sync_task_type <span class="hljs-keyword">as</span> <span class="hljs-keyword">object</span> (
    source_system    <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">50</span>),
    target_system    <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">50</span>),
    sync_type        <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">30</span>),
    record_count     <span class="hljs-built_in">number</span>,
    <span class="hljs-keyword">priority</span>         <span class="hljs-built_in">number</span>
);
</code></pre>
<h3 id="heading-4-queue-configuration">4. Queue Configuration</h3>
<p>The queue is configured with retry mechanisms:</p>
<pre><code class="lang-sql">  <span class="hljs-comment">-- Create queue table</span>
  dbms_aqadm.create_queue_table(
    queue_table        =&gt; 'AQ_DEMO.SYNC_QUEUE_TABLE',
    queue_payload_type =&gt; 'AQ_DEMO.SYNC_TASK_TYPE',
    multiple_consumers =&gt; false,
    <span class="hljs-keyword">comment</span>            =&gt; <span class="hljs-string">'Queue table for data synchronization tasks'</span>
  );

  <span class="hljs-comment">-- Create queue with retry configuration</span>
  dbms_aqadm.create_queue(
    queue_name     =&gt; 'AQ_DEMO.SYNC_QUEUE',
    queue_table    =&gt; 'AQ_DEMO.SYNC_QUEUE_TABLE',
    max_retries    =&gt; 5,        <span class="hljs-comment">-- Maximum number of retry attempts</span>
    retry_delay    =&gt; 30,       <span class="hljs-comment">-- Delay in seconds between retries</span>
    retention_time =&gt; 3600,     <span class="hljs-comment">-- Retention time in seconds (1 hour)</span>
    <span class="hljs-keyword">comment</span>        =&gt; <span class="hljs-string">'Queue for data synchronization tasks with retry configuration'</span>
  );

  <span class="hljs-comment">--</span>
  <span class="hljs-keyword">begin</span>
    <span class="hljs-comment">-- Start the queue</span>
    dbms_aqadm.start_queue(<span class="hljs-string">'AQ_DEMO.SYNC_QUEUE'</span>);
  <span class="hljs-keyword">end</span>;
</code></pre>
<p><strong>Retry Configuration Benefits:</strong></p>
<ul>
<li><strong>Automatic retry</strong> for failed messages</li>
<li><strong>Configurable delays</strong> to avoid overwhelming systems</li>
<li><strong>Message retention</strong> for debugging and auditing</li>
</ul>
<h3 id="heading-5-automatic-message-processing">5. Automatic Message Processing</h3>
<p>The callback procedure processes messages automatically:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">replace</span> <span class="hljs-keyword">procedure</span> sync_callback(
    <span class="hljs-keyword">context</span>   <span class="hljs-keyword">in</span> <span class="hljs-keyword">raw</span>
  , reginfo   <span class="hljs-keyword">in</span> sys.aq$_reg_info
  , <span class="hljs-keyword">descr</span>     <span class="hljs-keyword">in</span> sys.aq$_descriptor
  , payloadl  <span class="hljs-keyword">in</span> <span class="hljs-built_in">number</span>
  , payload   <span class="hljs-keyword">in</span> <span class="hljs-built_in">varchar2</span>
) <span class="hljs-keyword">as</span>
  l_message sync_task_type;
  l_dequeue_options dbms_aq.dequeue_options_t;
  l_message_props   dbms_aq.message_properties_t;
  l_message_id      raw(16);

  l_sync_type       varchar2(100);
<span class="hljs-keyword">begin</span>
  dbms_output.put_line(<span class="hljs-string">'Sync callback triggered at '</span> || to_char(systimestamp, <span class="hljs-string">'HH24:MI:SS'</span>));

  <span class="hljs-comment">-- Configure dequeue options</span>
  <span class="hljs-comment">-- WAIT: Controls how long to wait for messages</span>
  <span class="hljs-comment">--   • NO_WAIT: Return immediately if no message available</span>
  <span class="hljs-comment">--   • FOREVER: [default] Wait indefinitely for a message</span>
  <span class="hljs-comment">--   • Number: Wait specified seconds (0-4294967295)</span>
  l_dequeue_options.wait := dbms_aq.no_wait;

  <span class="hljs-comment">-- NAVIGATION: Which message to retrieve from queue</span>
  <span class="hljs-comment">--   • FIRST_MESSAGE: [default] Get first available message</span>
  <span class="hljs-comment">--   • NEXT_MESSAGE: Get next message in sequence</span>
  <span class="hljs-comment">--   • FIRST_MESSAGE_MULTI_GROUP: First message across consumer groups</span>
  <span class="hljs-comment">--   • NEXT_MESSAGE_MULTI_GROUP: Next message across consumer groups</span>
  <span class="hljs-comment">--l_dequeue_options.navigation := dbms_aq.first_message;</span>

  <span class="hljs-comment">-- VISIBILITY: When dequeue operation becomes visible to other transactions</span>
  <span class="hljs-comment">--   • ON_COMMIT: [default] Changes visible only after transaction commit</span>
  <span class="hljs-comment">--   • IMMEDIATE: Changes visible immediately</span>
  l_dequeue_options.visibility := dbms_aq.on_commit;

  <span class="hljs-comment">-- DEQUEUE_MODE: What happens to the message after dequeue</span>
  <span class="hljs-comment">--   • BROWSE: Read message but keep it in queue</span>
  <span class="hljs-comment">--   • LOCKED: Lock message for exclusive access</span>
  <span class="hljs-comment">--   • REMOVE: [default] Delete message after reading</span>
  <span class="hljs-comment">--   • REMOVE_NODATA: Delete message but don't return payload</span>
  l_dequeue_options.dequeue_mode := dbms_aq.remove;

  <span class="hljs-comment">-- DELIVERY_MODE: How messages are stored and delivered</span>
  <span class="hljs-comment">--   • PERSISTENT: [default] Messages stored in database tables (durable)</span>
  <span class="hljs-comment">--   • BUFFERED: Messages kept in memory (faster)</span>
  <span class="hljs-comment">--   • PERSISTENT_OR_BUFFERED: Use either mode as appropriate</span>
  <span class="hljs-comment">--l_dequeue_options.delivery_mode := dbms_aq.persistent;</span>

  <span class="hljs-comment">-- Additional dequeue options available:</span>
  <span class="hljs-comment">-- l_dequeue_options.consumer_name := 'consumer_name';  -- Target specific consumer in multi-consumer queues</span>
  <span class="hljs-comment">-- l_dequeue_options.msgid := raw_message_id;           -- Dequeue specific message by its unique ID</span>
  <span class="hljs-comment">-- l_dequeue_options.correlation := 'correlation_id';   -- Filter messages by correlation identifier</span>
  <span class="hljs-comment">-- l_dequeue_options.deq_condition := 'priority &gt; 5';   -- SQL WHERE condition to filter which messages to dequeue</span>
  <span class="hljs-comment">-- l_dequeue_options.transformation := 'transform_name'; -- Apply transformation function to message payload before returning</span>

  <span class="hljs-comment">-- Available descriptor properties for logging/debugging:</span>
  <span class="hljs-comment">-- descr.msg_id          - Message ID (RAW)</span>
  <span class="hljs-comment">-- descr.queue_name      - Queue name where message is located</span>
  <span class="hljs-comment">-- descr.consumer_name   - Consumer name (for multi-consumer queues)</span>
  <span class="hljs-comment">-- <span class="hljs-doctag">Note:</span> msg_priority and msg_state are not directly available on descriptor</span>


  logger.log('msg_id: ' || descr.msg_id);
<span class="hljs-comment">/*   logger.log('queue_name: ' || descr.queue_name);
  logger.log('consumer_name: ' || descr.consumer_name);
  logger.log('payload: ' || payload);
  logger.log('payloadl: ' || payloadl);
   */</span>
  l_dequeue_options.msgid := descr.msg_id;

  <span class="hljs-comment">-- Dequeue the message</span>
  dbms_aq.dequeue(
      queue_name         =&gt; 'AQ_DEMO.SYNC_QUEUE'
    , dequeue_options    =&gt; l_dequeue_options
    , message_properties =&gt; l_message_props
    , payload            =&gt; l_message
    , msgid              =&gt; l_message_id
  );
  l_sync_type := l_message.sync_type;

  <span class="hljs-comment">-- Process the sync task</span>
  <span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> sync_operations (source_system, target_system, sync_type, record_count, sync_status)
  <span class="hljs-keyword">values</span> (l_message.source_system, l_message.target_system, l_message.sync_type, l_message.record_count, <span class="hljs-string">'COMPLETED'</span>);

  dbms_output.put_line('Sync operation completed: ' || l_message.source_system || ' -&gt; ' || l_message.target_system);
  <span class="hljs-comment">--commit;</span>
exception
  when others then
    <span class="hljs-comment">--rollback;</span>
    dbms_output.put_line('Error in sync callback: ' || sqlerrm);

    raise;
<span class="hljs-keyword">end</span>;
</code></pre>
<h3 id="heading-6-regiter-the-callback">6. Regiter the callBack</h3>
<pre><code class="lang-sql"><span class="hljs-comment">-- When our queue SYNC_QUEUE will receibe an element,</span>
<span class="hljs-comment">-- this PLSQL is going to triger</span>
<span class="hljs-keyword">begin</span>
  dbms_aq.register(
      sys.aq$_reg_info_list(
          sys.aq$_reg_info(
              <span class="hljs-string">'AQ_DEMO.SYNC_QUEUE'</span>
            , dbms_aq.namespace_aq
            , <span class="hljs-string">'PLSQL://AQ_DEMO.SYNC_CALLBACK'</span>
            , <span class="hljs-literal">null</span>
          )
      )
    , <span class="hljs-number">1</span>
  );
  dbms_output.put_line('Sync notification registered');
<span class="hljs-keyword">end</span>;
/
</code></pre>
<h3 id="heading-8-start-the-queue">8. Start the queue</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">begin</span>
  <span class="hljs-comment">-- Start the queue</span>
  dbms_aqadm.start_queue(<span class="hljs-string">'AQ_DEMO.SYNC_QUEUE'</span>);
<span class="hljs-keyword">end</span>;
/
</code></pre>
<h3 id="heading-8-queue-management-api">8. Queue Management API</h3>
<p>A simple procedure to queue sync tasks:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">replace</span> <span class="hljs-keyword">procedure</span> queue_sync_task(
    p_source_system <span class="hljs-keyword">in</span> <span class="hljs-built_in">varchar2</span>
  , p_target_system <span class="hljs-keyword">in</span> <span class="hljs-built_in">varchar2</span>
  , p_sync_type     <span class="hljs-keyword">in</span> <span class="hljs-built_in">varchar2</span>
  , p_record_count  <span class="hljs-keyword">in</span> <span class="hljs-built_in">number</span>
  , p_priority      <span class="hljs-keyword">in</span> <span class="hljs-built_in">number</span> <span class="hljs-keyword">default</span> <span class="hljs-number">5</span>
) <span class="hljs-keyword">as</span>
  l_enqueue_options dbms_aq.enqueue_options_t;
  l_message_props   dbms_aq.message_properties_t;
  l_message         sync_task_type;
  l_msgid           raw(16);
<span class="hljs-keyword">begin</span>
  dbms_output.put_line(<span class="hljs-string">'Queueing sync task: '</span> || p_source_system || <span class="hljs-string">' -&gt; '</span> || p_target_system);

  <span class="hljs-comment">-- Create sync task message</span>
  l_message := sync_task_type(
      source_system =&gt; p_source_system,
      target_system =&gt; p_target_system,
      sync_type     =&gt; p_sync_type,
      record_count  =&gt; p_record_count,
      priority      =&gt; p_priority
  );

  <span class="hljs-comment">-- Configure enqueue options</span>
  l_enqueue_options.visibility := dbms_aq.on_commit;

  <span class="hljs-comment">-- Enqueue the sync task</span>
  dbms_aq.enqueue(
      queue_name         =&gt; 'AQ_DEMO.SYNC_QUEUE'
    , enqueue_options    =&gt; l_enqueue_options
    , message_properties =&gt; l_message_props
    , payload            =&gt; l_message
    , msgid              =&gt; l_msgid
  );

  <span class="hljs-keyword">commit</span>;
  dbms_output.put_line('Sync task queued successfully');

exception
  when others then
    <span class="hljs-keyword">rollback</span>;
    dbms_output.put_line('Error queueing sync task: ' || sqlerrm);
    raise;
<span class="hljs-keyword">end</span>;
</code></pre>
<h2 id="heading-usage-examples">Usage Examples</h2>
<h3 id="heading-queuing-sync-tasks">Queuing Sync Tasks</h3>
<pre><code class="lang-sql"><span class="hljs-comment">-- Queue different types of sync operations</span>
<span class="hljs-keyword">begin</span>
    queue_sync_task(<span class="hljs-string">'CRM_SYSTEM'</span>, <span class="hljs-string">'DATA_WAREHOUSE'</span>, <span class="hljs-string">'INCREMENTAL'</span>, <span class="hljs-number">1250</span>, <span class="hljs-number">3</span>);
    queue_sync_task('ERP_SYSTEM', 'ANALYTICS_DB', 'FULL', 50000, 5);
    queue_sync_task('WEB_APP', 'BACKUP_SYSTEM', 'DELTA', 340, 1);
<span class="hljs-keyword">end</span>;
</code></pre>
<h3 id="heading-monitoring-operations">Monitoring Operations</h3>
<pre><code class="lang-sql"><span class="hljs-comment">-- Check completed sync operations</span>
<span class="hljs-keyword">select</span> source_system
      , target_system
      , sync_type
      , record_count
      , sync_status
      , created_on
   <span class="hljs-keyword">from</span> sync_operations
  <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> created_on <span class="hljs-keyword">desc</span>;
</code></pre>
<h3 id="heading-queue-status-monitoring">Queue Status Monitoring</h3>
<pre><code class="lang-sql"><span class="hljs-comment">-- Monitor queue status and retry counts</span>
<span class="hljs-keyword">select</span> <span class="hljs-keyword">treat</span>(<span class="hljs-keyword">user_data</span> <span class="hljs-keyword">as</span> sync_task_type).source_system
     , <span class="hljs-keyword">treat</span>(<span class="hljs-keyword">user_data</span> <span class="hljs-keyword">as</span> sync_task_type).target_system
     , enq_time
     , deq_time
     , retry_count
     , msg_state
  <span class="hljs-keyword">from</span> aq$sync_queue_table
 <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> enq_time <span class="hljs-keyword">desc</span>;
</code></pre>
<h2 id="heading-benefits-of-this-approach">Benefits of This Approach</h2>
<h3 id="heading-1-reliability">1. <strong>Reliability</strong></h3>
<ul>
<li>Automatic retry mechanisms for failed operations</li>
<li>Message persistence ensures no data loss</li>
<li>Transaction-safe operations</li>
</ul>
<h3 id="heading-2-scalability">2. <strong>Scalability</strong></h3>
<ul>
<li>Asynchronous processing doesn't block source systems</li>
<li>Multiple consumers can process messages in parallel, This is a different example, where the multiple_consumers = TRUE and you need to register consumers; this example is only a single consumer.</li>
<li>Queue-based architecture handles load spikes</li>
</ul>
<h2 id="heading-real-world-applications">Real-World Applications</h2>
<p>This pattern is particularly useful for:</p>
<ul>
<li><strong>ETL Processes</strong>: Triggering data warehouse updates</li>
<li><strong>Microservices</strong>: Event-driven communication</li>
<li><strong>Data Replication</strong>: Keeping systems in sync</li>
<li><strong>Backup Operations</strong>: Asynchronous backup triggers</li>
<li><strong>Analytics</strong>: Real-time data feed processing</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Oracle Advanced Queuing provides a robust foundation for building reliable data synchronization systems. The combination of automatic retry mechanisms, message persistence, and flexible configuration makes it an excellent choice for enterprise integration scenarios.</p>
<p>The implementation shown here demonstrates how to create a production-ready sync system that can handle various types of data synchronization tasks while providing comprehensive monitoring and error handling capabilities.</p>
<hr />
<h2 id="heading-references-and-further-reading">References and Further Reading</h2>
<h3 id="heading-official-oracle-documentation">Official Oracle Documentation</h3>
<ul>
<li><a target="_blank" href="https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/advanced-queuing-AQ-types.html">Oracle Database 21c: Advanced Queuing Types</a> - Comprehensive AQ type definitions</li>
<li><a target="_blank" href="https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/DBMS_AQ.html">Oracle Database 21c: DBMS_AQ Package</a> - Complete API reference</li>
<li><a target="_blank" href="https://docs.oracle.com/database/121/ADQUE/aq_opers.htm#ADQUE2852">Oracle Database 12c: AQ Operations Guide</a> - Advanced queue operations</li>
</ul>
<h3 id="heading-tutorials-and-examples">Tutorials and Examples</h3>
<ul>
<li><a target="_blank" href="https://oracle-base.com/articles/9i/advanced-queuing-9i">Oracle-Base: Advanced Queuing in Oracle Database</a> - <strong>Base tutorial</strong> that inspired this implementation</li>
<li><a target="_blank" href="https://markhoxey.wordpress.com/2016/03/01/asynchronous-processing-using-aq-callback/">Asynchronous Processing using AQ Callback</a> - Callback implementation patterns</li>
</ul>
<h3 id="heading-source-code">Source Code</h3>
<ul>
<li><a target="_blank" href="https://github.com/Aftorres02/blog_app_examples/blob/master/post_2025/20_DBMS_AQ/simple_queue_example.sql">Complete Implementation on GitHub</a> - Full working code example with retry mechanisms and callback processing</li>
</ul>
<h3 id="heading-troubleshooting">Troubleshooting</h3>
<ul>
<li><a target="_blank" href="https://stackoverflow.com/questions/30502535/oracle-advance-queue-dequeue-not-working">Stack Overflow: Oracle AQ Dequeue Issues</a> - Common dequeue problems and solutions</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Methods to Convert HTML to PDF in Oracle APEX]]></title><description><![CDATA[Converting HTML content to PDF is a common requirement in web applications. This blog post demonstrates three different approaches to achieve this in Oracle APEX applications, each with its own advantages and use cases.
🔗 Live Demo
Overview of Metho...]]></description><link>https://en.aflorestorres.com/methods-to-convert-html-to-pdf-in-oracle-apex</link><guid isPermaLink="true">https://en.aflorestorres.com/methods-to-convert-html-to-pdf-in-oracle-apex</guid><category><![CDATA[#oracle-apex]]></category><category><![CDATA[Apex]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Apex Tips]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[orclapex]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Mon, 29 Sep 2025 05:21:32 GMT</pubDate><content:encoded><![CDATA[<p>Converting HTML content to PDF is a common requirement in web applications. This blog post demonstrates three different approaches to achieve this in Oracle APEX applications, each with its own advantages and use cases.</p>
<p><strong>🔗 <a target="_blank" href="https://oracleapex.com/ords/f?p=32431:1210:102704750023317:::::">Live Demo</a></strong></p>
<h2 id="heading-overview-of-methods">Overview of Methods</h2>
<ul>
<li><strong>Method 1: jsPDF with Fixed Dimensions</strong> - Simple, predictable PDF generation with predefined page sizes</li>
<li><strong>Method 2: jsPDF with Dynamic Scaling</strong> - Intelligent content adaptation with automatic orientation and scaling</li>
<li><strong>Method 3: Browser Print Method</strong> - Leverages browser's native PDF generation capabilities</li>
</ul>
<h2 id="heading-database-setup">Database Setup</h2>
<p>First, create the table to store HTML documents. For the complete DDL script, see <a target="_blank" href="html_tmp_table.sql">html_tmp_table.sql</a>.</p>
<h2 id="heading-required-libraries">Required Libraries</h2>
<p>Add these libraries to your APEX page (Page &gt; JavaScript &gt; File URLs):</p>
<pre><code>https:<span class="hljs-comment">//github.com/Aftorres02/blog_app_examples/tree/master/post_2025/18_tmp_html_to_pdf_dow/libs</span>
</code></pre><p><strong>Library References:</strong></p>
<ul>
<li><strong>jsPDF Documentation</strong>: <a target="_blank" href="https://github.com/parallax/jsPDF">https://github.com/parallax/jsPDF</a></li>
<li><strong>html2canvas Documentation</strong>: <a target="_blank" href="https://github.com/niklasvh/html2canvas">https://github.com/niklasvh/html2canvas</a></li>
<li><strong>Oracle APEX Documentation</strong>: <a target="_blank" href="https://docs.oracle.com/en/database/oracle/application-express/">https://docs.oracle.com/en/database/oracle/application-express/</a></li>
</ul>
<h2 id="heading-apex-process">APEX Process</h2>
<p>Create an AJAX process named <code>GET_HTML_CONTENT</code>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span>
    l_html_content <span class="hljs-keyword">clob</span>;
    l_document_id number;
<span class="hljs-keyword">begin</span>
    l_document_id := apex_application.g_x01;

    <span class="hljs-keyword">select</span> html_content, document_name
    <span class="hljs-keyword">into</span> l_html_content, :P1210_DOCUMENT_NAME
    <span class="hljs-keyword">from</span> html_documents
    <span class="hljs-keyword">where</span> <span class="hljs-keyword">id</span> = l_document_id
    <span class="hljs-keyword">and</span> active_yn = <span class="hljs-string">'Y'</span>;

    apex_json.open_object;
    apex_json.write('html_content', l_html_content);
    apex_json.write('document_name', :P1210_DOCUMENT_NAME);
    apex_json.close_object;

exception
    when no_data_found then
        apex_json.open_object;
        apex_json.write('error', 'Document not found');
        apex_json.close_object;
<span class="hljs-keyword">end</span>;
</code></pre>
<h2 id="heading-method-1-jspdf-with-fixed-dimensions">Method 1: jsPDF with Fixed Dimensions</h2>
<p><strong>What it does:</strong> Creates a PDF with predefined dimensions (900x630 points) and converts HTML content directly to PDF format. This method provides good control over page size but may not automatically adjust to content dimensions.</p>
<pre><code class="lang-javascript">apex.server.process(
  <span class="hljs-string">"GET_HTML_CONTENT"</span>,
  {
    <span class="hljs-attr">x01</span>: apex.item(<span class="hljs-string">"P1210_DOCUMENT_ID"</span>).getValue(),
  },
  {
    <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>) </span>{
      <span class="hljs-keyword">var</span> doc = <span class="hljs-keyword">new</span> jspdf.jsPDF(<span class="hljs-string">"p"</span>, <span class="hljs-string">"pt"</span>, [<span class="hljs-number">900</span>, <span class="hljs-number">630</span>]);

      doc.html(data.html_content, {
        <span class="hljs-attr">callback</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">doc</span>) </span>{
          <span class="hljs-keyword">if</span> (doc.getNumberOfPages() &gt;= <span class="hljs-number">2</span>) {
            doc.deletePage(<span class="hljs-number">2</span>);
          }
          <span class="hljs-keyword">var</span> filename = data.document_name;
          doc.save(filename + <span class="hljs-string">".pdf"</span>);
        },

        <span class="hljs-comment">// Set the rendering starting point and size</span>
        <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">y</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">margin</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">width</span>: <span class="hljs-number">590</span>, <span class="hljs-comment">// FULL width of the page in points</span>
        <span class="hljs-attr">windowWidth</span>: <span class="hljs-number">590</span>, <span class="hljs-comment">// Optional: helps html2canvas understand layout</span>

        <span class="hljs-attr">html2canvas</span>: {
          <span class="hljs-attr">useCORS</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">allowTaint</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">scale</span>: <span class="hljs-number">1</span>, <span class="hljs-comment">// Adjust this for quality vs performance</span>
        },
      });
    },
    <span class="hljs-attr">error</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      alert(<span class="hljs-string">"Error loading document content"</span>);
    },
  }
);
</code></pre>
<h2 id="heading-method-2-jspdf-with-dynamic-orientation-and-scaling">Method 2: jsPDF with Dynamic Orientation and Scaling</h2>
<p><strong>What it does:</strong> Automatically detects content dimensions and chooses the best orientation (portrait/landscape) and scaling factor to fit the content perfectly within the PDF page. This method provides the most intelligent content adaptation.</p>
<pre><code class="lang-javascript">apex.server.process(
  <span class="hljs-string">"GET_HTML_CONTENT"</span>,
  {
    <span class="hljs-attr">x01</span>: apex.item(<span class="hljs-string">"P1210_DOCUMENT_ID"</span>).getValue(),
  },
  {
    <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>) </span>{
      <span class="hljs-comment">// Create a hidden container dynamically</span>
      <span class="hljs-keyword">let</span> tempContainer = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
      tempContainer.id = <span class="hljs-string">"htmlContentHidden"</span>;
      <span class="hljs-built_in">document</span>.body.appendChild(tempContainer);

      <span class="hljs-comment">// Insert HTML content</span>
      tempContainer.innerHTML = data.html_content;

      <span class="hljs-comment">// Measure content dimensions</span>
      <span class="hljs-keyword">const</span> contentWidth = tempContainer.scrollWidth;
      <span class="hljs-keyword">const</span> contentHeight = tempContainer.scrollHeight;

      <span class="hljs-comment">// Decide orientation based on aspect ratio</span>
      <span class="hljs-keyword">let</span> orientation = contentWidth &gt; contentHeight ? <span class="hljs-string">"l"</span> : <span class="hljs-string">"p"</span>;

      <span class="hljs-comment">// Init jsPDF with dynamic orientation</span>
      <span class="hljs-keyword">const</span> { jsPDF } = <span class="hljs-built_in">window</span>.jspdf;
      <span class="hljs-keyword">let</span> doc = <span class="hljs-keyword">new</span> jsPDF(orientation, <span class="hljs-string">"pt"</span>, <span class="hljs-string">"a4"</span>);

      <span class="hljs-comment">// Get actual page dimensions from jsPDF</span>
      <span class="hljs-keyword">const</span> pageWidth = doc.internal.pageSize.getWidth();
      <span class="hljs-keyword">const</span> pageHeight = doc.internal.pageSize.getHeight();

      <span class="hljs-comment">// Calculate scale so content fits into page width (max = 1)</span>
      <span class="hljs-keyword">const</span> scaleFactor = <span class="hljs-built_in">Math</span>.min((pageWidth - <span class="hljs-number">40</span>) / contentWidth, <span class="hljs-number">1</span>);

      <span class="hljs-comment">// Generate PDF</span>
      doc.html(tempContainer, {
        <span class="hljs-attr">callback</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">doc</span>) </span>{
          doc.save(<span class="hljs-string">"dynamic_fit.pdf"</span>);
          <span class="hljs-comment">// Clean up</span>
          <span class="hljs-built_in">document</span>.body.removeChild(tempContainer);
        },
        <span class="hljs-attr">x</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">y</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">width</span>: pageWidth - <span class="hljs-number">40</span>, <span class="hljs-comment">// usable width with margins</span>
        <span class="hljs-attr">windowWidth</span>: contentWidth, <span class="hljs-comment">// original HTML width</span>
        <span class="hljs-attr">html2canvas</span>: {
          <span class="hljs-attr">scale</span>: scaleFactor,
          <span class="hljs-attr">useCORS</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">allowTaint</span>: <span class="hljs-literal">true</span>,
        },
      });
    },
    <span class="hljs-attr">error</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      alert(<span class="hljs-string">"Error loading document content"</span>);
    },
  }
);
</code></pre>
<h2 id="heading-method-3-browser-print-method">Method 3: Browser Print Method</h2>
<p><strong>What it does:</strong> Opens the HTML content in a new browser window and triggers the browser's native print dialog, allowing users to save as PDF using their browser's built-in PDF functionality. This method leverages the browser's native PDF generation capabilities.</p>
<pre><code class="lang-javascript">apex.server.process(
  <span class="hljs-string">"GET_HTML_CONTENT"</span>,
  {
    <span class="hljs-attr">x01</span>: apex.item(<span class="hljs-string">"P1210_DOCUMENT_ID"</span>).getValue(),
  },
  {
    <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>) </span>{
      <span class="hljs-keyword">var</span> printWindow = <span class="hljs-built_in">window</span>.open(<span class="hljs-string">''</span>, <span class="hljs-string">'_blank'</span>, <span class="hljs-string">'width=800,height=600'</span>);
      printWindow.document.write(<span class="hljs-string">'&lt;!DOCTYPE html&gt;'</span>);
      printWindow.document.write(<span class="hljs-string">'&lt;html&gt;&lt;head&gt;&lt;title&gt;Document&lt;/title&gt;'</span>);
      printWindow.document.write(<span class="hljs-string">'&lt;style&gt;body{font-family:Arial,sans-serif;margin:20px;}&lt;/style&gt;'</span>);
      printWindow.document.write(<span class="hljs-string">'&lt;/head&gt;&lt;body&gt;'</span>);
      printWindow.document.write(data.html_content);
      printWindow.document.write(<span class="hljs-string">'&lt;/body&gt;&lt;/html&gt;'</span>);
      printWindow.document.close();

      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
          printWindow.print();
          <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
              printWindow.close();
          }, <span class="hljs-number">1000</span>);
      }, <span class="hljs-number">500</span>);
    },
    <span class="hljs-attr">error</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      alert(<span class="hljs-string">"Error loading document content"</span>);
    },
  }
);
</code></pre>
<h2 id="heading-comparison-of-methods">Comparison of Methods</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>Pros</td><td>Cons</td><td>Best For</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Method 1: Fixed Dimensions</strong></td><td>Simple, predictable output</td><td>May not fit all content</td><td>Standard documents</td></tr>
<tr>
<td><strong>Method 2: Dynamic Scaling</strong></td><td>Intelligent content adaptation</td><td>More complex code</td><td>Variable content sizes</td></tr>
<tr>
<td><strong>Method 3: Browser Print</strong></td><td>No external libraries needed</td><td>Requires user interaction</td><td>Simple implementations</td></tr>
</tbody>
</table>
</div><hr />
]]></content:encoded></item><item><title><![CDATA[Capturar Region/pantalla - Print Screen in Oracle APEX]]></title><description><![CDATA[Amigos, en esta oportunidad veremos algo simple pero muchas veces útil, cuando deseamos capturar la pantalla, es decir tomar una imagen o también llamado printscreen.
]
Para esto usaremos la famosa librería de JavaScript html2canvas.
Veremos dos form...]]></description><link>https://en.aflorestorres.com/capturar-regionpantalla-print-screen-in-oracle-apex</link><guid isPermaLink="true">https://en.aflorestorres.com/capturar-regionpantalla-print-screen-in-oracle-apex</guid><category><![CDATA[Apex]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Oracle]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Wed, 22 May 2024 03:30:40 GMT</pubDate><content:encoded><![CDATA[<p>Amigos, en esta oportunidad veremos algo simple pero muchas veces útil, cuando deseamos capturar la pantalla, es decir tomar una imagen o también llamado printscreen.</p>
<p><img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2024/2024_print_scrren/demo_printscreen.gif" alt="demoPrint" />]</p>
<p>Para esto usaremos la famosa librería de JavaScript html2canvas.</p>
<p>Veremos dos formas de agregar esta librería a APEX, aunque particularmente recomendare la segunda.</p>
<p>La primera forma es agregando el CDN (url) de esta librería  https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js directamente a la APEX.</p>
<p><img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2024/2024_print_scrren/CDN.png" alt="cdn_library" /></p>
<p>Y la segunda forma que recomiendo, es descargar el archivo JS desde la URL antes mencionada y cargarlo directamente en los static files de APEX</p>
<p>Luego usaremos esa ruta directamente en APEX, con ello evitamos la dependencia de un servidor externo en caso exista o no el link.</p>
<p><img src="https://raw.githubusercontent.com/Aftorres02/blog_app_examples/master/post_2024/2024_print_scrren/app_library.png" alt="app_library" /></p>
<p>Finalmente, para el ejemplo, demo que la pueden encontrar <a target="_blank" href="https://apex.oracle.com/pls/apex/f?p=32431:1040:::NO">aquí</a></p>
<p>Hemos creado un reporte y un botón, el botón tendrá una acción dinámica para ejecutar el siguiente JavaScript</p>
<p>Lo importante aquí es definir el ID de la región ah capturar, o en su defecto si queremos capturar toda la pantalla, solo sería cambio del selector.</p>
<p>Y el otro punto es el nombre y la descarga.</p>
<pre><code class="lang-js">  <span class="hljs-comment">// Specified the region to print</span>
  <span class="hljs-comment">// this example can be region report or any region</span>
  <span class="hljs-keyword">var</span> region = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'employeeReport'</span>);
  <span class="hljs-comment">// or can be all document</span>
  <span class="hljs-keyword">var</span> documentBod = <span class="hljs-built_in">document</span>.body;

  <span class="hljs-comment">// Use html2canvas to capture the region</span>
  html2canvas(region).then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">canvas</span>) </span>{
  <span class="hljs-comment">// html2canvas(documentBod).then(function(canvas) {</span>

    <span class="hljs-comment">// Convierte el canvas a una URL de datos en formato PNG</span>
    <span class="hljs-keyword">var</span> imgData = canvas.toDataURL(<span class="hljs-string">"image/png"</span>);

    <span class="hljs-comment">// create a temporary link for download the image</span>
    <span class="hljs-keyword">var</span> link = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'a'</span>);
    link.href = imgData;

    <span class="hljs-keyword">var</span> imageCaptureName = <span class="hljs-string">'captura.png'</span>;
    link.download = imageCaptureName;
    <span class="hljs-built_in">document</span>.body.appendChild(link);
    link.click();
    <span class="hljs-built_in">document</span>.body.removeChild(link);
  }).catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>) </span>{
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error capturando la región:'</span>, error);
  });
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Capturar onChange/cell edit en Interactive Grid y asignar valor en celda]]></title><description><![CDATA[El siguiente blog surge en la necesidad de un compañero que me relato el siguiente problema.
Se tiene un Interactive Grid, donde se muestran proyectos, aquí están padres e hijos, se muestra una columna llamada BUDGET, esto significa que varias líneas...]]></description><link>https://en.aflorestorres.com/capturar-onchangecell-edit-en-interactive-grid-y-asignar-valor-en-celda</link><guid isPermaLink="true">https://en.aflorestorres.com/capturar-onchangecell-edit-en-interactive-grid-y-asignar-valor-en-celda</guid><category><![CDATA[#oracle-apex]]></category><category><![CDATA[Apex]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[interactive-grid]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[javascript framework]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Sat, 18 May 2024 16:14:35 GMT</pubDate><content:encoded><![CDATA[<p>El siguiente blog surge en la necesidad de un compañero que me relato el siguiente problema.
Se tiene un Interactive Grid, donde se muestran proyectos, aquí están padres e hijos, se muestra una columna llamada BUDGET, esto significa que varias líneas en este grid tienen en común un proyecto padre, que también se visualiza en el grid, la diferente es que los proyectos padre no tienen un ID de proyecto padre, con eso se desea que cada vez que se edite el valor de BUDGET en una celda hija, este sume el valor(budget) de todas las celdas hijas de un proyecto y asignarlo la celda (budget) del proyecto padre.</p>
<p>En este ejemplo detallaremos los siguientes puntos, usando Interactive Grids:</p>
<ul>
<li>Como capturar el onChange o cambio de valor en una celda.</li>
<li>Obtener la celda donde se ejecutó el cambio, así como el valor antiguo y nuevo.</li>
<li>Calcular en base a la celda seleccionada valores de otras columnas.</li>
<li>Asignación de valores a una fila y columna especifica.</li>
</ul>
<p>Todo el requerimientos y acciones de logro en un bloque de dos funciones JavaScript, en donde usamos el modelo, un listener para escuchar los eventos de cambio de valor y así mismo un loop para recorrer las filas , sumar y asignar a la celda correspondiente.</p>
<p>La demo de este ejemplo lo pueden visualizar en... <a target="_blank" href="https://apex.oracle.com/pls/apex/f?p=32431:1050:::NO">DEMO</a></p>
<p>Primero creamos un InteractiveGrid y le asignamos un ID en mi caso <em>employeesIG</em>.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_grid_edit/B1.png" alt="regionID" /></p>
<p>Lo siguiente será agregar el siguiente código JavaScript en la sección de la página, <strong>Function and Global Variable Declaration</strong></p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_grid_edit/B2.png" alt="js_declaration" /></p>
<pre><code class="lang-js">
<span class="hljs-keyword">var</span> igGrid$ = $(<span class="hljs-string">"#employeesIG"</span>);
igGrid$.on(<span class="hljs-string">"interactivegridviewmodelcreate"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event, ui</span>) </span>{

  <span class="hljs-keyword">var</span> grid = apex.region(<span class="hljs-string">"employeesIG"</span>).widget().interactiveGrid(<span class="hljs-string">"getViews"</span>, <span class="hljs-string">"grid"</span>);
  <span class="hljs-keyword">var</span> model = grid.model;

  <span class="hljs-comment">//var model = ui.model; // we can also use this definnnnnn</span>
  <span class="hljs-keyword">var</span> model = model;
  <span class="hljs-comment">//  console.log('ui',ui);</span>

    <span class="hljs-keyword">if</span> (ui.viewId === <span class="hljs-string">"grid"</span>) {

        sid = model.subscribe( {

          <span class="hljs-attr">onChange</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">type, change</span>) </span>{

            <span class="hljs-keyword">if</span> (type === <span class="hljs-string">'set'</span> &amp;&amp;  (change.field == <span class="hljs-string">'BUDGET'</span> ) ) {

                <span class="hljs-keyword">var</span> record = model.getRecord(change.recordId), <span class="hljs-comment">// Usar change.recordId</span>
                    fieldName = change.field,
                    newValue = model.getValue(record, fieldName);
                    parentProjectId = model.getValue(record, <span class="hljs-string">'PARENT_PROJECT_ID'</span>);
                    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'change'</span>,change);

                    lastRecord = record;
                <span class="hljs-comment">//console.log('Celda cambiada en la columna:', fieldName,'oldValue', change.oldValue, 'Nuevo Valor:', newValue);</span>
                <span class="hljs-keyword">if</span>(parentProjectId){
                  <span class="hljs-comment">// the timeout important, this is a bug but not document 100%</span>
                  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
                    calculateTotals(model,parentProjectId);
                  },<span class="hljs-number">1</span>);
                }
            }
            <span class="hljs-comment">// console.log(15, ui.viewId, type, change, event,ui);</span>
          }

        });

    }

});


<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateTotals</span>(<span class="hljs-params">model, selectedParentId</span>)</span>{

  <span class="hljs-keyword">let</span> sumParentProject = <span class="hljs-number">0</span>;

  model.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">fetchRecord, index, idx</span>) </span>{


    <span class="hljs-comment">// save the parent ID of the curren fetchRow</span>
    <span class="hljs-keyword">let</span> parentProjectId = model.getValue(fetchRecord,<span class="hljs-string">'PARENT_PROJECT_ID'</span>);
    <span class="hljs-comment">// define the ID for get he parent id later</span>
    <span class="hljs-keyword">let</span> fetchSelectedID= model.getValue(fetchRecord,<span class="hljs-string">'ID'</span>);

    <span class="hljs-comment">// sum all the records with the same parent ID</span>
    <span class="hljs-keyword">if</span> (selectedParentId == parentProjectId ){
      <span class="hljs-comment">// get the current budget</span>
      <span class="hljs-keyword">let</span> budget = model.getValue(fetchRecord, <span class="hljs-string">'BUDGET'</span> );
      sumParentProject = <span class="hljs-built_in">Number</span>(sumParentProject) + <span class="hljs-built_in">Number</span>(budget);
    }

    <span class="hljs-comment">// reuse the loop for search the parent record</span>
    <span class="hljs-keyword">if</span> (fetchSelectedID == selectedParentId) {
      parentRecord = fetchRecord;
    }
  });

  <span class="hljs-comment">// cast to String to apply the changes on the view</span>
  model.setValue(parentRecord, <span class="hljs-string">'BUDGET'</span> , sumParentProject.toString() );

  <span class="hljs-comment">//console.log('this is the parent record', parentRecord);</span>
  <span class="hljs-comment">//console.log('the total for projectId(' + parentId + ') is', sumParentProject);</span>

}
</code></pre>
<p>Y como resultado vemos que al cambiar el valor de la celda, este se replica en la celda padre.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_grid_edit/demo.gif" alt="demo" /></p>
]]></content:encoded></item><item><title><![CDATA[APEX-JS NameSpaces, orden y limpieza en nuestro código]]></title><description><![CDATA[Como hemos visto la necesidad de incluir JavaScript dentro APEX en varias oscaciones, es escencial mantener un orden dentro del codigo que escribamos.
Es por ello que existe el concepto de namespaces en Javascript, para poder mantener un orden.
Dejar...]]></description><link>https://en.aflorestorres.com/apex-js-namespaces-orden-y-limpieza-en-nuestro-codigo</link><guid isPermaLink="true">https://en.aflorestorres.com/apex-js-namespaces-orden-y-limpieza-en-nuestro-codigo</guid><category><![CDATA[apex javascript]]></category><category><![CDATA[Apex]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[good practices]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Mon, 06 May 2024 05:38:32 GMT</pubDate><content:encoded><![CDATA[<p>Como hemos visto la necesidad de incluir JavaScript dentro APEX en varias oscaciones, es escencial mantener un orden dentro del codigo que escribamos.</p>
<p>Es por ello que existe el concepto de namespaces en Javascript, para poder mantener un orden.</p>
<p>Dejaré un ejemplo de como podriamos usar esto, ya sea creacion diferentes objectos y agregando funciones o directamente una funcion dentro de un submodul.</p>
<pre><code class="lang-js"><span class="hljs-comment">/*
Global Variables across all files
*/</span>

<span class="hljs-keyword">var</span> currentUser = <span class="hljs-string">'ANGEL'</span>;


<span class="hljs-comment">// This code simulates to be existing in a different file for example file called page1</span>
<span class="hljs-comment">// This example creates first an object and then the functions below  this object</span>
<span class="hljs-keyword">var</span> namespace = namespace || {};

<span class="hljs-comment">/** ==========================================================================
* @module page 1
**/</span>
namespace.page1 = {
  <span class="hljs-attr">globalVarP1</span>: <span class="hljs-string">'This is a page 1'</span>,

  <span class="hljs-comment">/** ==========================================================================

  * <span class="hljs-doctag">@function <span class="hljs-variable">calculator</span></span>
  * This could be usefully to separate different functionalities  in the same file or for organice better the code
  *
  * <span class="hljs-doctag">@example </span>namespace.page1.calculator('Angel').sum(2,30)
  * <span class="hljs-doctag">@example </span>namespace.page1.calculator('Angel').multi(2,30)
  * <span class="hljs-doctag">@example </span>namespace.page1.calculator('Angel').description
  * <span class="hljs-doctag">@example </span>namespace.page1.calculator('Angel') you could use this call for initialize some parameters for example
  *
  * <span class="hljs-doctag">@issue</span>
  *
  * <span class="hljs-doctag">@author <span class="hljs-variable">Angel</span></span>
  * <span class="hljs-doctag">@created </span>5 May 2024
  * <span class="hljs-doctag">@return  </span>return {
  *           description: "This function contains subFunctions to simulate a calculator",
  *           sum: sum,
  *           div: div,
  *           multi: multi,
  *          };
  **/</span>
  <span class="hljs-attr">calculator</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">fucntionGlobalVariable</span>) </span>{

    <span class="hljs-comment">// private variables</span>
    <span class="hljs-keyword">var</span> internal_msg = <span class="hljs-string">"Calculator of page 1"</span>;

    <span class="hljs-comment">// private functions</span>
    <span class="hljs-keyword">var</span> privateFunction = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">return</span> fucntionGlobalVariable + <span class="hljs-string">"/"</span> + internal_msg;
    };

    <span class="hljs-comment">// public functions</span>
    <span class="hljs-keyword">var</span> sum = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">a,b</span>) </span>{
      <span class="hljs-keyword">return</span> privateFunction() + <span class="hljs-string">" - sum is: "</span>
           + (a + b);
    };

    <span class="hljs-keyword">var</span> div = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">a,b</span>) </span>{
      <span class="hljs-keyword">return</span> privateFunction() + <span class="hljs-string">" - division is: "</span>
           + (a / b);
    };

    <span class="hljs-keyword">var</span> multi = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">a,b</span>) </span>{
      <span class="hljs-keyword">return</span> privateFunction() + <span class="hljs-string">" - multiplication is: "</span>
           + (a * b);
    };

    <span class="hljs-comment">// Return things that is going to be public</span>
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">description</span>: <span class="hljs-string">"This function contains subFunctions to simulate a calculator"</span>,
      <span class="hljs-attr">sum</span>: sum,
      <span class="hljs-attr">div</span>: div,
      <span class="hljs-attr">multi</span>: multi,
    };
  }
};


<span class="hljs-comment">/**
==========================================================================
==========================================================================
**/</span>

<span class="hljs-comment">// This code simulate to be existing in a different file for example file called page2</span>
<span class="hljs-comment">// In this example we are creating the direct function, without an object</span>

<span class="hljs-keyword">var</span> namespace = namespace || {};

<span class="hljs-comment">/**
* @module page 2
**/</span>


  <span class="hljs-comment">/** ==========================================================================

  * @function calculator
  * This could be usefully to separate different funcionalities in the same file or for organize better the code
  *
  * @example namespace.page2
  * @example namespace.page2.publicFunction()
  * @example namespace.page2.globalVarP2
  *
  * @issue
  *
  * @author Angel
  * @created Monday, 5 May 2024
  * @return  return {
  *            description: internal_msg,
  *            publicFunction: publicFunction,
  *            globalVarP2: globalVarP2,
  *          }
  **/</span>
namespace.page2 = (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">namespace, $, undefined</span>) </span>{

  <span class="hljs-keyword">var</span> globalVarP2 = <span class="hljs-string">'This is a page 2'</span>;

  <span class="hljs-keyword">var</span> internal_msg = <span class="hljs-string">"This is an internal message of page 2"</span>;

  <span class="hljs-keyword">var</span> privateFunction = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> globalVarP2 + <span class="hljs-string">" / "</span> + internal_msg;
  };
  <span class="hljs-keyword">var</span> publicFunction = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'This is a public function with private function: '</span> + privateFunction();
  };

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">description</span>: internal_msg,
    <span class="hljs-attr">publicFunction</span>: publicFunction,
    <span class="hljs-attr">globalVarP2</span>: globalVarP2,
  };

})(namespace, apex.jQuery);
</code></pre>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_js_namespaces/apex_js_namespaces.png" alt="prueba_calls_js" /></p>
<p>Aqui algunas referencias:</p>
<ul>
<li><a target="_blank" href="https://www.scaler.com/topics/javascript-namespace/">javascript-namespace</a></li>
<li><a target="_blank" href="https://addyosmani.com/blog/essential-js-namespacing/">essential-js-namespacing</a></li>
<li><a target="_blank" href="https://www.linkedin.com/pulse/javascript-namespaces-joseph-evans/m">javascript-namespaces-joseph-evans</a></li>
<li><a target="_blank" href="https://vmorneau.me/avoid-javascript-mess/">avoid-javascript-mess</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[apex.server.process, AJAX Calls, Promise y Async, Await - Ejemplos prácticos]]></title><description><![CDATA[Hola amigos en este blog veremos cómo hacer llamadas Ajax consecutivas usando apex.server.process , es decir una tras otra.
Iremos de frente a los ejemplos, para ver un poco la teoría dejare algunos enlaces en la parte final, entendamos que ya tenemo...]]></description><link>https://en.aflorestorres.com/apexserverprocess-ajax-calls-promise-y-async-await-ejemplos-practicos</link><guid isPermaLink="true">https://en.aflorestorres.com/apexserverprocess-ajax-calls-promise-y-async-await-ejemplos-practicos</guid><category><![CDATA[apex js]]></category><category><![CDATA[Apex]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Apex Tips]]></category><category><![CDATA[Ajax]]></category><category><![CDATA[#oracle-apex]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Thu, 02 May 2024 04:32:23 GMT</pubDate><content:encoded><![CDATA[<p>Hola amigos en este blog veremos cómo hacer llamadas Ajax consecutivas usando apex.server.process , es decir una tras otra.</p>
<p>Iremos de frente a los ejemplos, para ver un poco la teoría dejare algunos enlaces en la parte final, entendamos que ya tenemos un proceso ajax callback definido en nuestra página, en mi caso se llamará <code>apex_ajax_call</code></p>
<p><a target="_blank" href="https://apex.oracle.com/pls/apex/f?p=32431:1020:::NO">Demo</a></p>
<h3 id="heading-async-await">Async / Await</h3>
<pre><code class="lang-js">
<span class="hljs-keyword">var</span> async_fnc_1 = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">parameter</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sleep for: '</span> + parameter + <span class="hljs-string">' sec'</span>);
  <span class="hljs-keyword">return</span> apex.server.process( <span class="hljs-string">"apex_ajax_call"</span>, {
    <span class="hljs-attr">x01</span>: parameter
  }
  , { <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> data </span>)  </span>{<span class="hljs-comment">// do something here</span>
      }
  }
  );
}

<span class="hljs-keyword">var</span> async_fnc_2 = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">parameter</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sleep for: '</span> + parameter + <span class="hljs-string">' sec'</span>);
  <span class="hljs-comment">//throw new Error('Something went wrong.');</span>
  <span class="hljs-keyword">return</span> apex.server.process( <span class="hljs-string">"apex_ajax_call"</span>, {
    <span class="hljs-attr">x01</span>: parameter
  }
  , { <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> data </span>)  </span>{<span class="hljs-comment">// do something here</span>
          data.xx = <span class="hljs-string">'new param just in case'</span>;
          <span class="hljs-comment">//throw new Error(' custom error');</span>
      }
  }
  );
}

<span class="hljs-keyword">var</span> async_fnc_3 = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">parameter</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sleep for: '</span> + parameter + <span class="hljs-string">' sec'</span>);
  <span class="hljs-keyword">return</span> apex.server.process( <span class="hljs-string">"apex_ajax_call"</span>, {
    <span class="hljs-attr">x01</span>: parameter
  }
  , { <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> data </span>)  </span>{<span class="hljs-comment">// do something here</span>
      }
  }
  );
}

<span class="hljs-keyword">var</span> fncError = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">dataError</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error on promise'</span> + dataError);
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">exec_calls</span>(<span class="hljs-params"></span>)</span>{
  <span class="hljs-keyword">var</span> result1 = <span class="hljs-keyword">await</span> async_fnc_1(<span class="hljs-number">2</span>);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'call 1: '</span>, result1);

  <span class="hljs-keyword">var</span> result2 = <span class="hljs-keyword">await</span> async_fnc_2(<span class="hljs-number">5</span>);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'call 2: '</span>, result2);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'print custom result: '</span>, result2.xx);

  <span class="hljs-keyword">var</span> result3 = <span class="hljs-keyword">await</span> async_fnc_3(<span class="hljs-number">3</span>);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'call 3: '</span>, result3);
}

exec_calls();
</code></pre>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_ajax_async_promise/apex_async_await.gif" alt="ajax_async_await" /></p>
<h3 id="heading-promise">Promise</h3>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> promise_1 = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">parameter</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sleep for: '</span> + parameter + <span class="hljs-string">' sec'</span>);
  <span class="hljs-keyword">return</span> apex.server.process( <span class="hljs-string">"apex_ajax_call"</span>, {
    <span class="hljs-attr">x01</span>: parameter
  }
  , { <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> data </span>)  </span>{<span class="hljs-comment">// do something here</span>
      }
  }
  );
}

<span class="hljs-keyword">var</span> promise_2 = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">parameter</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sleep for: '</span> + parameter + <span class="hljs-string">' sec'</span>);
  <span class="hljs-comment">//throw new Error('Something went wrong.');</span>
  <span class="hljs-keyword">return</span> apex.server.process( <span class="hljs-string">"apex_ajax_call"</span>, {
    <span class="hljs-attr">x01</span>: parameter
  }
  , { <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> data </span>)  </span>{<span class="hljs-comment">// do something here</span>
          data.xx = <span class="hljs-string">'new param just in case'</span>;
          <span class="hljs-comment">//throw new Error(' custom error');</span>
      }
  }
  );
}

<span class="hljs-keyword">var</span> promise_3 = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">parameter</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sleep for: '</span> + parameter + <span class="hljs-string">' sec'</span>);
  <span class="hljs-keyword">return</span> apex.server.process( <span class="hljs-string">"apex_ajax_call"</span>, {
    <span class="hljs-attr">x01</span>: parameter
  }
  , { <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> data </span>)  </span>{<span class="hljs-comment">// do something here</span>
      }
  }
  );
}

<span class="hljs-keyword">var</span> fncError = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">dataError</span>)</span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error on promise'</span> + dataError);
}

promise_1(<span class="hljs-number">3</span>).then(
  <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'result promise 1 / '</span>,data.message);
    <span class="hljs-keyword">return</span> promise_2(<span class="hljs-number">2</span>);
  }
  , fncError
).then(
  <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'result promise 2 / '</span>,data);
    <span class="hljs-keyword">return</span> promise_3(<span class="hljs-number">1</span>);
  }
  , fncError
).then(
  <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'result promise 3 / '</span>,data.message); 
  }
  , fncError
)
</code></pre>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_ajax_async_promise/apex_promise_demo.gif" alt="ajax_promise" /></p>
<h3 id="heading-definicion-del-ejemplo">Definicion del ejemplo</h3>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_ajax_async_promise/js_ajax_call.png" alt="img_ajax_callback" /></p>
<p>Código PlSql</p>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span>
  l_param       <span class="hljs-built_in">number</span> := apex_application.g_x01;
<span class="hljs-keyword">begin</span>

  DBMS_SESSION.sleep(l_param);

  apex_json.open_object;
  apex_json.write('success',true);
  apex_json.write('message','sleep: ' || l_param || ' at: '|| LOCALTIMESTAMP);
  apex_json.close_object;

exception
  when others then
    apex_json.open_object;
    apex_json.write('success',false);
    apex_json.close_object;
<span class="hljs-keyword">end</span>;
</code></pre>
<h4 id="heading-referencias">Referencias</h4>
<ul>
<li><a target="_blank" href="https://vmorneau.me/javascript-promises-in-apex/">Javascript promises in apex</a></li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=vvZJDobsm5M">Promesas, AJAX, PLSQL y Javascript</a></li>
<li><a target="_blank" href="https://vmorneau.me/javascript-async-await/">Javascript async await</a></li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=yyuoIcF7hPI">Using apex.server.process for AJAX in Oracle APEX</a></li>
<li><a target="_blank" href="https://docs.oracle.com/en/database/oracle/apex/23.2/aexjs/apex.server.html#.process">Apex.server.process</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Personalizando el Popup Menu - Oracle APEX]]></title><description><![CDATA[En este blog extenderemos las funcionalidad del popup Menu de Oracle APEX, permitiendo agregar y modificar botónes de acción de forma dinámica y condicional a nuestros requerimientos.
Como base principal tendremos una menu popup generado a través de ...]]></description><link>https://en.aflorestorres.com/personalizando-el-popup-menu-oracle-apex</link><guid isPermaLink="true">https://en.aflorestorres.com/personalizando-el-popup-menu-oracle-apex</guid><category><![CDATA[menu popup]]></category><category><![CDATA[Apex]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[popupmenu]]></category><category><![CDATA[Oracle]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Mon, 25 Mar 2024 03:38:45 GMT</pubDate><content:encoded><![CDATA[<p>En este blog extenderemos las funcionalidad del popup Menu de Oracle APEX, permitiendo agregar y modificar botónes de acción de forma dinámica y condicional a nuestros requerimientos.</p>
<p>Como base principal tendremos una menu popup generado a través de una Lista estática del sharecomponents de APEX.</p>
<h3 id="heading-1-creando-el-popup-menu">1) Creando el Popup Menu</h3>
<ol>
<li>Creamos una lista en APEX con el siguiente SQL. Si tú ya tienes una lista puedes pasar al paso de: Agregando un nuevo botón de acción.</li>
</ol>
<pre><code class="lang-sql">
<span class="hljs-keyword">with</span> menu_test <span class="hljs-keyword">as</span> (
  <span class="hljs-keyword">select</span> <span class="hljs-number">10</span> <span class="hljs-keyword">id</span>, <span class="hljs-string">'New'</span>        <span class="hljs-keyword">as</span> entry_text, <span class="hljs-string">'#'</span> <span class="hljs-keyword">as</span> entry_target, <span class="hljs-string">'fa-file-o'</span> <span class="hljs-keyword">as</span> entry_image  <span class="hljs-keyword">from</span> dual <span class="hljs-keyword">union</span> <span class="hljs-keyword">all</span>
  <span class="hljs-keyword">select</span> <span class="hljs-number">20</span> <span class="hljs-keyword">id</span>, <span class="hljs-string">'Open'</span>       <span class="hljs-keyword">as</span> entry_text, <span class="hljs-string">'javascript:console.log(''test parent'')'</span> <span class="hljs-keyword">as</span> entry_target, <span class="hljs-string">'fa-folder-open-o'</span> <span class="hljs-keyword">as</span> entry_image  <span class="hljs-keyword">from</span> dual <span class="hljs-keyword">union</span> <span class="hljs-keyword">all</span>
  <span class="hljs-keyword">select</span> <span class="hljs-number">30</span> <span class="hljs-keyword">id</span>, <span class="hljs-string">'---'</span>        <span class="hljs-keyword">as</span> entry_text, <span class="hljs-string">'separator'</span> <span class="hljs-keyword">as</span> entry_target, <span class="hljs-string">''</span> <span class="hljs-keyword">as</span> entry_image  <span class="hljs-keyword">from</span> dual <span class="hljs-keyword">union</span> <span class="hljs-keyword">all</span>
  <span class="hljs-keyword">select</span> <span class="hljs-number">40</span> <span class="hljs-keyword">id</span>, <span class="hljs-string">'Exit'</span>       <span class="hljs-keyword">as</span> entry_text, <span class="hljs-string">'#'</span> <span class="hljs-keyword">as</span> entry_target, <span class="hljs-string">''</span> <span class="hljs-keyword">as</span> entry_image  <span class="hljs-keyword">from</span> dual <span class="hljs-keyword">union</span> <span class="hljs-keyword">all</span>
  <span class="hljs-keyword">select</span> <span class="hljs-number">50</span> <span class="hljs-keyword">id</span>, <span class="hljs-string">'Spreadsheet'</span><span class="hljs-keyword">as</span> entry_text, <span class="hljs-string">'#'</span> <span class="hljs-keyword">as</span> entry_target, <span class="hljs-string">'fa-file-excel-o'</span> <span class="hljs-keyword">as</span> entry_image  <span class="hljs-keyword">from</span> dual <span class="hljs-keyword">union</span> <span class="hljs-keyword">all</span>
  <span class="hljs-keyword">select</span> <span class="hljs-number">60</span> <span class="hljs-keyword">id</span>, <span class="hljs-string">'Image'</span>      <span class="hljs-keyword">as</span> entry_text, <span class="hljs-string">'#'</span> <span class="hljs-keyword">as</span> entry_target, <span class="hljs-string">'fa-file-image-o'</span> <span class="hljs-keyword">as</span> entry_image  <span class="hljs-keyword">from</span> dual
)
<span class="hljs-keyword">select</span> <span class="hljs-literal">null</span>
     , <span class="hljs-string">'ID:'</span>||<span class="hljs-keyword">id</span>|| <span class="hljs-string">' - '</span> || entry_text label
     , entry_target target
     , <span class="hljs-literal">null</span> is_current
     , entry_image image
     , <span class="hljs-string">'width="20" height="20"'</span> image_attrib
     , <span class="hljs-string">'image_alt'</span> image_alt
     , <span class="hljs-keyword">id</span> a01 <span class="hljs-comment">-- this place is for display the ID</span>
  <span class="hljs-keyword">from</span> menu_test
</code></pre>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_menu_popup_more_actions/list.png" alt="list" /></p>
<ol>
<li>Ahora creas un botón y una región con la siguiente configuración.</li>
</ol>
<p>Lo importante son los puntos marcados en rojo, ya que los IDs nos serviran de selector luego.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_menu_popup_more_actions/b_2.png" alt="imagen_b2" />
<img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_menu_popup_more_actions/b_3.png" alt="imagen_b3" />
<img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_menu_popup_more_actions/b_1.png" alt="imagen_b1" /></p>
<h3 id="heading-agregando-un-nuevo-boton-de-accion">Agregando un nuevo botón de acción</h3>
<p>Ahora para agregar un botón de forma dinámica nos apoyaremos de los siguientes blogs.</p>
<ul>
<li><a target="_blank" href="https://lmoreaux.hashnode.dev/oracle-apex-menu-popup-joelkallmanday">Oracle APEX menu popup, Louis Moreaux</a></li>
<li><p><a target="_blank" href="https://docs.oracle.com/en/database/oracle/apex/23.2/aexjs/menu.html#event:beforeOpen">Oracle JS Doc</a></p>
</li>
<li><p><a target="_blank" href="https://hardlikesoftware.com/weblog/2015/07/13/apex-5-0-custom-menus/">Custom menus</a></p>
</li>
</ul>
<p>Este control soporta diferentes opciones entre ellas el evento,</p>
<p>Esto nos permite la manipulación del elemento antes de mostrarlo, una de las primeras cosas que haremos será agregar una nueva acción a la lista actual.</p>
<p>Para el ejemplo agregaremos el botón tipo= <strong>acción</strong> y label= <strong>Link Demo</strong></p>
<pre><code class="lang-js"><span class="hljs-comment">// Usamos el ID que le dimos a nuestra región del popup menu, para seleccionar el elemento</span>
$( <span class="hljs-string">"#customized_menu"</span> ).menu({

  <span class="hljs-comment">// creamos un "listener" del evento beforeOpen</span>
  <span class="hljs-attr">beforeOpen</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> event, ui </span>) </span>{

    <span class="hljs-comment">// adicional console para ver la información que se tiene cuando se habre el menu</span>
    <span class="hljs-built_in">console</span>.log(event, ui);

    <span class="hljs-comment">// condicional para verificar si el botón ya existe y no volverlo agregar</span>
    <span class="hljs-comment">// para este caso los IDs con muy importantes para diferenciarlos</span>
    <span class="hljs-comment">// El type, label, icon, action son totalmente personalizables de acuerdo a nuestras necesidades</span>
    <span class="hljs-keyword">if</span> ( !ui.menu.items.find(<span class="hljs-function"><span class="hljs-params">objeto</span> =&gt;</span> objeto.id === <span class="hljs-string">'myIdDemo'</span>) ){
            ui.menu.items.push({<span class="hljs-attr">id</span>: <span class="hljs-string">'myIdDemo'</span>
                              , <span class="hljs-attr">type</span>: <span class="hljs-string">'action'</span>
                              , <span class="hljs-attr">label</span>: <span class="hljs-string">'Link Demo'</span>
                              , <span class="hljs-attr">icon</span>: <span class="hljs-string">'fa-badge-check'</span>
                              , <span class="hljs-attr">action</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
                                  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Click en el nuevo botón'</span>);
                                }
                              })
    }

  }

});
</code></pre>
<p>Para completar la demostración agregue un botón que usa una acción dinámica y ejecuta el javascript.</p>
<p>Muestra una alerta al finalizar</p>
<p>Y al probar el botón de acciones vemos como se agrego una nueva acción.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_menu_popup_more_actions/imagen_resultadoGif.gif" alt="imagen_resultadoGif" /></p>
<h3 id="heading-modificando-un-atributo-en-todos-los-elementos">Modificando un atributo en todos los elementos</h3>
<p>Solo como ejemplo podremos hacer algo asi, cambiar un solo atributo a todos los elementos.</p>
<ul>
<li><p>Revisar los comentarios del código siguiente
```js
// Usamos el ID que le dimos a nuestra región del popup menu, para seleccionar el elemento
$( "#customized_menu" ).menu({</p>
<p>// creamos un "listener" del evento beforeOpen
beforeOpen: function( event, ui ) {</p>
<p>  // guardamos la colección de items para luego recorrer uno por uno
  var menuItems = ui.menu.items;
  menuItems = menuItems.map( function ( item ) {
    //por cada elemento cambiamos el icono a fa-bug
    item.icon =  'fa-bug';
    // retornamos el item modificado
    return item;
  });</p>
<p>  }</p>
</li>
</ul>
<p>});</p>
<pre><code>
![cambiar_todos_los_iconos]


### Modificando acciones específicas #<span class="hljs-number">1</span> (ubicadas por el ID)


Como podemos ver nuestro popup menu del ejemplo ya tiene un ID único, esto para poder encontrar de manera fácil cual elemento deseamos modificar.

Seleccionaremos un ID especifico y le cambiaremos el tipo, Icono, Label en on y en off


<span class="hljs-string">``</span><span class="hljs-string">`js

// Usamos el ID que le dimos a nuestra región del popup menu, para seleccionar el elemento
$( "#customized_menu" ).menu({
  // creamos un "listener" del evento beforeOpen
  beforeOpen: function( event, ui ) {

    // guardamos la coleccion de items para luego recorrer uno por uno
    var menuItems = ui.menu.items;
    menuItems = menuItems.map( function ( item ) {
      // buscamos si es el ID 40 y modificamos sus propiedades
      if (item.id == 40 ){
        item.label =  'Botón cambiado';
        item.icon  =  'fa-ban';
      }
      return item;
    });

    }

});</span>
</code></pre><p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_menu_popup_more_actions/modificar_btn.gif" alt="modificar_btn" /></p>
<h3 id="heading-modificando-acciones-especificas-2-ubicadas-por-el-id">Modificando acciones específicas "2 (ubicadas por el ID)</h3>
<p>Seleccionaremos un ID especifico y le cambiaremos el tipo, Icono, Label en on y en off.
Este es un caso mas complejo ya que el tipo toggle maneja diferentes opciones, como vemos a continuación.</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> toggleGlobal = <span class="hljs-literal">true</span>;

<span class="hljs-comment">// Usamos el ID que le dimos a nuestra región del popup menu, para seleccionar el elemento</span>
$( <span class="hljs-string">"#customized_menu"</span> ).menu({
  <span class="hljs-comment">// creamos un "listener" del evento beforeOpen</span>
  <span class="hljs-attr">beforeOpen</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"> event, ui </span>) </span>{

    <span class="hljs-comment">// guardamos la coleccion de items para luego recorrer uno por uno</span>
    <span class="hljs-keyword">var</span> menuItems = ui.menu.items;

      menuItems = menuItems.map( <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"> item </span>) </span>{
      <span class="hljs-comment">// buscamos si es el ID 40 y modificamos sus propiedades</span>
      <span class="hljs-keyword">if</span> (item.id == <span class="hljs-number">40</span> ){
        item.type = <span class="hljs-string">"toggle"</span>;
        item.set = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
          <span class="hljs-comment">// e tiene el valor de true o false, dependiendo del estado del toggle</span>
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'set status'</span>,e);
          <span class="hljs-comment">// lo asignamos a nuestra variable global</span>
          toggleGlobal = e;
        }
        item.get = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e,f</span>) </span>{
         <span class="hljs-comment">// aquí devolvemos true o false, dependiendo que queremos mostrar</span>
         <span class="hljs-comment">// para el ejemplo retornamos el valor de nuestra variable global toggleGlobal1</span>
         <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'get'</span>,e,f,toggleGlobal);
         <span class="hljs-keyword">return</span> toggleGlobal;
       }
       <span class="hljs-comment">//item.label =  "toggle"; // si definimos label , será prioridad sobre el on / off label;</span>
       <span class="hljs-keyword">delete</span> item.label; <span class="hljs-comment">// remover la propiedad label definida inicialmente</span>
       item.onLabel =  <span class="hljs-string">"Toggle onLabel"</span>;
       item.offLabel =  <span class="hljs-string">"Toggle offLabel"</span>;
       item.onIcon = <span class="hljs-string">"fa-badge-check"</span>;
       item.offIcon = <span class="hljs-string">"fa-badge"</span>;
      }
      <span class="hljs-keyword">return</span> item;
    });

    }

});
</code></pre>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_menu_popup_more_actions/reemplazarToggle.gif" alt="imagen_popupMenu" /></p>
]]></content:encoded></item><item><title><![CDATA[Toggle en Popup Menu Oracle APEX]]></title><description><![CDATA[Este blog será un pequeño extracto de como customizar un popup menu en Oracle APEX.
Para esta oportunidad hablaremos sobre el tipo toggle
Ejemplo: blog

Como base nos guiaremos del blog de John Snyders donde crea un popup menu custom. APEX 5.0 Custom...]]></description><link>https://en.aflorestorres.com/toggle-en-popup-menu-oracle-apex</link><guid isPermaLink="true">https://en.aflorestorres.com/toggle-en-popup-menu-oracle-apex</guid><category><![CDATA[popupmenu]]></category><category><![CDATA[toggle]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[Apex]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Wed, 21 Feb 2024 05:38:29 GMT</pubDate><content:encoded><![CDATA[<p>Este blog será un pequeño extracto de como customizar un popup menu en Oracle APEX.
Para esta oportunidad hablaremos sobre el tipo <em>toggle</em></p>
<p>Ejemplo: <a target="_blank" href="https://apex.oracle.com/pls/apex/f?p=32431:1010::::::">blog</a></p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_toggle_menu_popup/demo.gif" alt="demo_gif" /></p>
<p>Como base nos guiaremos del blog de John Snyders donde crea un popup menu custom. <a target="_blank" href="https://hardlikesoftware.com/weblog/2015/07/13/apex-5-0-custom-menus/">APEX 5.0 Custom Menus</a></p>
<p>La documentación oficial del ítem y otras opciones están en <a target="_blank" href="https://docs.oracle.com/en/database/oracle/apex/23.2/aexjs/menu.html">Widget: menu</a> y un ejemplo de Oracle <a target="_blank" href="https://apex.oracle.com/pls/apex/f?p=42:1306">Menu Popup</a></p>
<h3 id="heading-consideraciones">Consideraciones</h3>
<ul>
<li>Como parte del ejemplo estamos agregando dos ítems para mostrar el resultado en cada acción set del toggle, ver line con <strong>apex.item("P1010_TOGGLE_1").setValue(e);</strong></li>
</ul>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_toggle_menu_popup/itemsDemo.png" alt="itemsDemo" /></p>
<ul>
<li>Se están usando dos botones de toggle, para ver la diferencia cuando se coloca el label o cuando queremos usar un label e icono diferente en on y off del toggle.</li>
</ul>
<p>Necesitaremos  un botón que abrirá el menu:</p>
<ul>
<li>Recordemos agregar el atributo <em>data-menu="actionsMenu"</em> donde <em>actionsMenu</em> será nuestro identificador para agregar los valores del menu luego</li>
</ul>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_toggle_menu_popup/ButtonConfig.png" alt="ButtonConfig" /></p>
<p>Ahora crearemos las opciones del menu , en la sección <em>Function and Global Variable Declaration</em> de APEX</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Creamos nuestra variable *toggleGlobal* que nos servirá para guardar el estado del toggle</span>
<span class="hljs-keyword">var</span> toggleGlobal1 = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">var</span> toggleGlobal2 = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">var</span> menu$ = $(<span class="hljs-string">"&lt;div id='actionsMenu'&gt;&lt;/div&gt;"</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_initMenuPopup</span>(<span class="hljs-params"></span>)  </span>{

  $(<span class="hljs-string">"body"</span>).append(menu$);
  menu$.menu({
  <span class="hljs-attr">iconType</span>: <span class="hljs-string">"fa"</span>,
  <span class="hljs-attr">items</span>: [
      {
        <span class="hljs-attr">id</span>:<span class="hljs-string">'toggleTest1'</span>
      , <span class="hljs-attr">type</span>: <span class="hljs-string">"toggle"</span>
      , <span class="hljs-attr">set</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
        <span class="hljs-comment">// e tiene el valor de true o false, dependiendo del estado del toggle</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'set status'</span>,e);
        <span class="hljs-comment">// lo asignamos a nuestra variable global</span>
        toggleGlobal1 = e;
        apex.item(<span class="hljs-string">"P1010_TOGGLE_1"</span>).setValue(e);
      }
     , <span class="hljs-attr">get</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e,f</span>) </span>{
        <span class="hljs-comment">// aquí devolvemos true o false, dependiendo que queremos mostrar</span>
        <span class="hljs-comment">// para el ejemplo retornamos el valor de nuestra variable global toggleGlobal1</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'get'</span>,e,f,toggleGlobal1);
        <span class="hljs-keyword">return</span> toggleGlobal1;
      }
     <span class="hljs-comment">//, label:  "toggle" // si definimos label , será prioridad sobre el on / off label</span>
     , <span class="hljs-attr">onLabel</span>:  <span class="hljs-string">"Toggle onLabel"</span>
     , <span class="hljs-attr">offLabel</span>:  <span class="hljs-string">"Toggle offLabel"</span>
     , <span class="hljs-attr">onIcon</span>: <span class="hljs-string">"fa-badge-check"</span>
     , <span class="hljs-attr">offIcon</span>: <span class="hljs-string">"fa-badge"</span>
     <span class="hljs-comment">//, disabled: false</span>
    },
    {
      <span class="hljs-attr">type</span>: <span class="hljs-string">"separator"</span>,
      <span class="hljs-attr">label</span>: <span class="hljs-string">"-"</span>,
      <span class="hljs-attr">href</span>: <span class="hljs-string">"separator"</span>
    } ,
    {
        <span class="hljs-attr">id</span>:<span class="hljs-string">'toggleTest1'</span>,
        <span class="hljs-attr">type</span>: <span class="hljs-string">"toggle"</span>
      , <span class="hljs-attr">label</span>:  <span class="hljs-string">"toggle"</span>
      , <span class="hljs-attr">set</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
        <span class="hljs-comment">// e tiene el valor de true o false, dependiendo del estado del toggle</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'set status'</span>,e);
        <span class="hljs-comment">// lo asignamos a nuestra variable global</span>
        toggleGlobal2 = e;
        apex.item(<span class="hljs-string">"P1010_TOGGLE_2"</span>).setValue(e);
        }
      , <span class="hljs-attr">get</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e,f</span>) </span>{
         <span class="hljs-comment">// aquí devolvemos true o false, dependiendo que queremos mostrar</span>
         <span class="hljs-comment">// para el ejemplo retornamos el valor de nuestra variable global toggleGlobal1</span>
         <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'get'</span>,e,f,toggleGlobal2);
         <span class="hljs-keyword">return</span> toggleGlobal2;
       }
      , <span class="hljs-attr">label</span>:  <span class="hljs-string">"toggle"</span> <span class="hljs-comment">// si definimos label , será prioridad sobre el on / off label</span>
      , <span class="hljs-attr">onLabel</span>:  <span class="hljs-string">"Toggle onLabel"</span>
      , <span class="hljs-attr">offLabel</span>:  <span class="hljs-string">"Toggle offLabel"</span>
      , <span class="hljs-attr">onIcon</span>: <span class="hljs-string">"fa-badge-check"</span>
      , <span class="hljs-attr">offIcon</span>: <span class="hljs-string">"fa-badge"</span>
      <span class="hljs-comment">//, disabled: false</span>
    }
  ]
  })
};
</code></pre>
<p>inicializamos nuestra función</p>
<pre><code class="lang-js">_initMenuPopup();
<span class="hljs-string">``</span><span class="hljs-string">`</span>
</code></pre>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2024_toggle_menu_popup/initFunct.png" alt="initFunct" /></p>
]]></content:encoded></item><item><title><![CDATA[Compartir Variables Globales entre aplicaciones en Oracle APEX]]></title><description><![CDATA[En este post hablaremos un poco de cómo usar variables globales.
Estas que son muy útiles cuando tenemos varias aplicaciones y deseamos compartir un mismo valor entre ellas, como el id del usuario logeado, un correo, un valor de impuesto, alguna noti...]]></description><link>https://en.aflorestorres.com/compartir-variables-globales-entre-aplicaciones-en-oracle-apex</link><guid isPermaLink="true">https://en.aflorestorres.com/compartir-variables-globales-entre-aplicaciones-en-oracle-apex</guid><category><![CDATA[Apex]]></category><category><![CDATA[Apex Tips]]></category><category><![CDATA[apex.world]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[Oracle Cloud]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Wed, 17 Jan 2024 05:02:52 GMT</pubDate><content:encoded><![CDATA[<p>En este post hablaremos un poco de cómo usar variables globales.
Estas que son muy útiles cuando tenemos varias aplicaciones y deseamos compartir un mismo valor entre ellas, como el id del usuario logeado, un correo, un valor de impuesto, alguna notificación, etc.</p>
<p>El primer paso será crear la variable global dentro de "shared components" para nuestro ejemplo es G_PET_NAME y lo marcamos como Global.</p>
<p>Debemos crear este ítem en todas las aplicaciones que deseamos compartir y con el mismo nombre.</p>
<p>Nota:
 Por seguridad dejamos "Sesión State Protection" como Restricted</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/1.png" alt="image_ref_1" /></p>
<p>Importante</p>
<p>Ahora para que ambas aplicaciones compartan, necesitamos que el Authentication Scheme
se comparta el mismo cookie.</p>
<p>De la misma forma esta configuración debe hacerse por igual en todas las aplicaciones donde se compartirá.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/2.png" alt="image_ref_2" /></p>
<h2 id="heading-test">Test</h2>
<p>Para hacer la prueba, crearemos 1/2 item en page 1, </p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/2_1.png" alt="image_ref_2_1" /></p>
<p>y en el submit (Button Save Values) asignaremos los valores de los ítems a las variables globales, <em>G_PET_NAME</em>, <em>G_PET_STATUS</em></p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/2_2.png" alt="image_ref_2_2" /></p>
<p>En la aplicación vemos que no tenemos valores en sesión...</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/3.png" alt="image_ref_3" /></p>
<p>Presionamos Guardar... y vemos los valores ahora en sesión.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/4.png" alt="image_ref_4" /></p>
<p>En la otra app, creamos los mismos ítems y la fuente será un <em>computation</em> que tomará el valor de las globales.</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/5.png" alt="image_ref_5" /></p>
<p>Si vemos el app en ejecución, veremos cómo se seteo el valor que ingresamos en el APP 1, en el APP 2</p>
<p><img src="https://github.com/Aftorres02/blog_app_examples/raw/master/post_2024/2023_variables_globales/5_1.png" alt="image_ref_5_1" /></p>
<p>Espero les sirva, recuerden lo importante es el <strong>COOKIE</strong></p>
]]></content:encoded></item><item><title><![CDATA[Formas de leer un JSON en Oracle - APEX_JSON]]></title><description><![CDATA[Formas de leer un JSON en Oracle - APEX_JSON
Formas de leer un JSON en Oracle

Usando JSON_TABLE

Usando APEX_JSON


Para leer un formato tipo JSON dentro de ORACLE existen diferentes formas una de ellas es usando APEX_JSON.
También podemos usar JSON...]]></description><link>https://en.aflorestorres.com/formas-de-leer-un-json-en-oracle-apexjson</link><guid isPermaLink="true">https://en.aflorestorres.com/formas-de-leer-un-json-en-oracle-apexjson</guid><category><![CDATA[Apex]]></category><category><![CDATA[#oracle-apex]]></category><category><![CDATA[Oracle Cloud]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[json]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Thu, 30 Mar 2023 03:45:11 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-formas-de-leer-un-json-en-oracle-apexjson">Formas de leer un JSON en Oracle - APEX_JSON</h1>
<h2 id="heading-formas-de-leer-un-json-en-oracle">Formas de leer un JSON en Oracle</h2>
<ul>
<li><p><a target="_blank" href="https://aflorestorres.com/2023/03/12/formas-de-leer-un-json-en-oracle-json_table/">Usando JSON_TABLE</a></p>
</li>
<li><p><a target="_blank" href="https://www.rittmanmead.com/blog/2018/08/parsing-badly-formatted-json-in-oracle-db-with-apex_json/">Usando APEX_JSON</a></p>
</li>
</ul>
<p>Para leer un formato tipo JSON dentro de ORACLE existen diferentes formas una de ellas es usando APEX_JSON.</p>
<p>También podemos usar JSON_TABLE como vemos en este otro blog.</p>
<p>En el siguiente blog daré algunas formas de leer los diferentes atributos usando PL-SQL.</p>
<p>Tomaremos de ejemplo el siguiente JSON.</p>
<p>En el script completo dejare toda la demo incluida la creación de la tabla para la prueba.</p>
<p><a target="_blank" href="https://drive.google.com/file/d/1I78vyN6E7-UJkPS0a6yFZyA-f5_9qlUF/view?usp=sharing">Script Ejemplo 1</a></p>
<p><a target="_blank" href="https://drive.google.com/file/d/1I859Xnb8VQ62UasmmM6w9qLTPdi5N_5F/view?usp=share_link">Script Ejemplo 2</a></p>
<p>JSON:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"squadName"</span>: <span class="hljs-string">"Super hero squad"</span>,
  <span class="hljs-attr">"active"</span>   : <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"attributes"</span> : {
                  <span class="hljs-attr">"formed"</span>    : <span class="hljs-number">2016</span>,
                  <span class="hljs-attr">"secretBase"</span>: <span class="hljs-string">"Super tower"</span>,
                  <span class="hljs-attr">"Address"</span>   : {
                                  <span class="hljs-attr">"city"</span>    : <span class="hljs-string">"South San Francisco"</span>,
                                  <span class="hljs-attr">"zipCode"</span> : <span class="hljs-number">99236</span>,
                                  <span class="hljs-attr">"country"</span> : <span class="hljs-string">"United States of America"</span>
                                }
  },
  <span class="hljs-attr">"members"</span>: [
    {
      <span class="hljs-attr">"name"</span>          : <span class="hljs-string">"Molecule Man"</span>,
      <span class="hljs-attr">"age"</span>           : <span class="hljs-number">29</span>,
      <span class="hljs-attr">"secretIdentity"</span>: <span class="hljs-string">"Dan Jukes"</span>,
      <span class="hljs-attr">"powers"</span>        : [<span class="hljs-string">"Radiation resistance"</span>,
                         <span class="hljs-string">"Turning tiny"</span>
                        ]
    },
    {
      <span class="hljs-attr">"name"</span>          : <span class="hljs-string">"Madame Uppercut"</span>,
      <span class="hljs-attr">"age"</span>           : <span class="hljs-number">39</span>,
      <span class="hljs-attr">"secretIdentity"</span>: <span class="hljs-string">"Jane Wilson"</span>,
      <span class="hljs-attr">"powers"</span>        : [<span class="hljs-string">"Million tonne punch"</span>
                        ]
    },
    {
      <span class="hljs-attr">"name"</span>          : <span class="hljs-string">"Eternal Flame"</span>,
      <span class="hljs-attr">"age"</span>           : <span class="hljs-number">1000000</span>,
      <span class="hljs-attr">"secretIdentity"</span>: <span class="hljs-literal">null</span>,
      <span class="hljs-attr">"powers"</span>        : [<span class="hljs-string">"Immortality"</span>,
                          <span class="hljs-string">"Heat Immunity"</span>,
                          <span class="hljs-string">"Inferno"</span>,
                          <span class="hljs-string">"Teleportation"</span>,
                          <span class="hljs-string">"Interdimensional travel"</span>
                        ]
    }
  ]
}
</code></pre>
<p>Empezaremos con determinar cuantas claves(Keys) tenemos en cada objeto.</p>
<pre><code class="lang-sql">
<span class="hljs-keyword">declare</span>
    l_json          demo_json.json_column%<span class="hljs-keyword">type</span>;
    l_cantidad      number;
    json_content    apex_json.t_values;
    l_members       apex_t_varchar2;
    l_sub_array     apex_t_varchar2;
<span class="hljs-keyword">begin</span>
  <span class="hljs-comment">-- retornamos el JSON de ejemplo dentro de l_json</span>
  <span class="hljs-keyword">select</span> json_column <span class="hljs-keyword">into</span> l_json <span class="hljs-keyword">from</span> demo_json;
  <span class="hljs-comment">-- paseamos el json con apex_json.parse</span>
  apex_json.parse(json_content, l_json);

  <span class="hljs-comment">-- cantidad de claves del objeto principal</span>
  l_cantidad := apex_json.get_count(p_path =&gt; '.' ,p_values =&gt; json_content);
  dbms_output.put_line ('Cantidad de claves : '); <span class="hljs-comment">-- 4 atributos</span>
  dbms_output.put_line ('.. en el objeto principal ' || l_cantidad ); <span class="hljs-comment">-- 4 atributos</span>
  dbms_output.put_line ('.. dentro del objeto "attributes" ' ||  apex_json.get_count(p_path=&gt; 'attributes'  , p_values =&gt; json_content ) );
  dbms_output.put_line ('.. dentro del objeto "attributes.Address" ' ||  apex_json.get_count(p_path=&gt; 'attributes.Address'  , p_values =&gt; json_content ) );

  l_members := apex_json.get_members(p_path=&gt; 'members[1]'  , p_values =&gt; json_content );
  dbms_output.put_line ('.. dentro del sub array  members[1]  ' || l_members.count  );

  <span class="hljs-comment">-- Note que aquí es muy diferente para sacar la cantidad de valores dentro del array power</span>
  l_sub_array := apex_json.get_t_varchar2 (p_path=&gt; 'members[3].powers'  , p_values =&gt; json_content );
  dbms_output.put_line ('.. dentro del sub array "members[1].powers" ' || l_sub_array.count );

  <span class="hljs-comment">-- igual diferente para obtener la cantidad de los objetos dentro del array members</span>
  l_cantidad := apex_json.get_count (p_path=&gt; 'members'  , p_values =&gt; json_content );
  dbms_output.put_line ('.. dentro del sub array "members" ' ||  l_cantidad );
<span class="hljs-keyword">end</span>;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1Joh-FIooz0LbQYq2xWw7j0jb5DJyMrWv" alt="run_code_1" /></p>
<p>A modo de práctica varemos como imprimir estas claves del JSON.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span>
    l_json          demo_json.json_column%<span class="hljs-keyword">type</span>;
    l_cantidad      number;
    json_content    apex_json.t_values;
    l_members       apex_t_varchar2;
    l_sub_array     apex_t_varchar2;
<span class="hljs-keyword">begin</span>
  <span class="hljs-comment">-- retornamos el JSON de ejemplo dentro de l_json</span>
  <span class="hljs-keyword">select</span> json_column <span class="hljs-keyword">into</span> l_json <span class="hljs-keyword">from</span> demo_json;
  <span class="hljs-comment">-- paseamos el json con apex_json.parse</span>
  apex_json.parse(json_content, l_json);

  <span class="hljs-comment">-- cantidad de claves del objeto principal</span>
  l_cantidad := apex_json.get_count(p_path =&gt; '.' ,p_values =&gt; json_content);
  dbms_output.put_line ('Cantidad de claves : '); <span class="hljs-comment">-- 4 atributos</span>
  dbms_output.put_line ('<span class="hljs-comment">-------------------------------------------' );</span>
  dbms_output.put_line ('.. en el objeto principal ' || l_cantidad ); <span class="hljs-comment">-- 4 atributos</span>

  l_members := apex_json.get_members(p_path=&gt; '.'  , p_values =&gt; json_content );
  for i in 1 .. l_cantidad
  loop
    dbms_output.put_line ('l_members: ' ||  l_members(i) );
  <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

  dbms_output.put_line ('<span class="hljs-comment">-------------------------------------------' );</span>
  dbms_output.put_line ('.. dentro del objeto "attributes" ' ||  apex_json.get_count(p_path=&gt; 'attributes'  , p_values =&gt; json_content ) );
  l_members := apex_json.get_members(p_path=&gt; 'attributes'  , p_values =&gt; json_content );
  for i in 1 .. l_members.count
  loop
    dbms_output.put_line ('attributes: ' ||  l_members(i) );
  <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

  dbms_output.put_line ('<span class="hljs-comment">-------------------------------------------' );</span>
  dbms_output.put_line ('.. dentro del objeto "attributes.Address" ' ||  apex_json.get_count(p_path=&gt; 'attributes.Address'  , p_values =&gt; json_content ) );
  l_members := apex_json.get_members(p_path=&gt; 'attributes.Address'  , p_values =&gt; json_content );
  for i in 1 .. l_members.count
  loop
    dbms_output.put_line ('attributes.Address: ' ||  l_members(i) );
  <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

  dbms_output.put_line ('<span class="hljs-comment">-------------------------------------------' );</span>
  l_members := apex_json.get_members(p_path=&gt; 'members[1]'  , p_values =&gt; json_content );
  dbms_output.put_line ('.. dentro del sub array  members[1]  ' || l_members.count  );
  for i in 1 .. l_members.count
  loop
    dbms_output.put_line ('members[1]: ' ||  l_members(i) );
  <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

  dbms_output.put_line ('<span class="hljs-comment">-------------------------------------------' );</span>
  <span class="hljs-comment">-- Note que aquí es muy diferente para sacar la cantidad de valores dentro del array power</span>
  l_sub_array := apex_json.get_t_varchar2 (p_path=&gt; 'members[3].powers'  , p_values =&gt; json_content );
  dbms_output.put_line ('.. dentro del sub array "members[1].powers" ' || l_sub_array.count );
  for i in 1 .. l_sub_array.count
  loop
    dbms_output.put_line ('members[3].powers: ' ||  l_sub_array(i) );
  <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

  dbms_output.put_line ('<span class="hljs-comment">-------------------------------------------' );</span>
  <span class="hljs-comment">-- Obtener la cantidad de los objetos dentro del array members</span>
  l_cantidad := apex_json.get_count (p_path=&gt; 'members'  , p_values =&gt; json_content );
  dbms_output.put_line ('.. dentro del sub array "members" ' ||  l_cantidad );
<span class="hljs-keyword">end</span>;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1Jq5RTTee_Ks93IHxwuQTFkoIZX7zx8Wz" alt="run_code_2" /></p>
<p>Ahora empezaremos a obtener los valores de cada nodo.</p>
<p>Primero los del objeto superior, como todos los elementos son objetos es más sencillo.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span>
    l_json          demo_json.json_column%<span class="hljs-keyword">type</span>;
    l_boolean       boolean;
    json_content    apex_json.t_values;

<span class="hljs-keyword">begin</span>
  <span class="hljs-comment">-- retornamos el JSON de ejemplo dentro de l_json</span>
  <span class="hljs-keyword">select</span> json_column <span class="hljs-keyword">into</span> l_json <span class="hljs-keyword">from</span> demo_json;
  <span class="hljs-comment">-- paseamos el json con apex_json.parse</span>
  apex_json.parse(json_content, l_json);

  dbms_output.put_line ('<span class="hljs-comment">----------------  Nodo Principal  ---------------------------' );</span>
  dbms_output.put_line ('squadName : ' || apex_json.get_varchar2(p_path =&gt; 'squadName' ,p_values =&gt; json_content) );
  dbms_output.put_line ('active : ' ||  apex_json.get_varchar2 (p_path =&gt; 'active' ,p_values =&gt; json_content));
  l_boolean := apex_json.get_boolean (p_path =&gt; 'active' ,p_values =&gt; json_content);
  dbms_output.put_line ('active : ' ||  case when l_boolean = true then 'value true' else 'value false' <span class="hljs-keyword">end</span> );

  dbms_output.put_line ('<span class="hljs-comment">----------------  Sub Nodo attributes ---------------------------' );</span>
  dbms_output.put_line ('formed : ' || apex_json.get_varchar2(p_path =&gt; 'attributes.formed' ,p_values =&gt; json_content) );
  dbms_output.put_line ('secretBase : ' || apex_json.get_varchar2(p_path =&gt; 'attributes.secretBase' ,p_values =&gt; json_content) );

  dbms_output.put_line ('<span class="hljs-comment">----------------- Sub Nodo Address --------------------------' );</span>
  dbms_output.put_line ('city : '    || apex_json.get_varchar2(p_path =&gt; 'attributes.Address.city' ,p_values =&gt; json_content) );
  dbms_output.put_line ('zipCode : '    || apex_json.get_number(p_path =&gt; 'attributes.Address.zipCode' ,p_values =&gt; json_content) );
  dbms_output.put_line ('country : '    || apex_json.get_varchar2(p_path =&gt; 'attributes.Address.country' ,p_values =&gt; json_content) );

<span class="hljs-keyword">end</span>;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1Jqa7QVdLRNUB8A7BRPy0VJS-hpJZbtAc" alt="run_code_3" /></p>
<p>Para el array de datos necesitaremos hacer un loop.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">declare</span>
    l_json          demo_json.json_column%<span class="hljs-keyword">type</span>;
    l_boolean       boolean;
    json_content    apex_json.t_values;
    l_cantidad      number;
    l_members       apex_t_varchar2;
<span class="hljs-keyword">begin</span>
  <span class="hljs-comment">-- retornamos el JSON de ejemplo dentro de l_json</span>
  <span class="hljs-keyword">select</span> json_column <span class="hljs-keyword">into</span> l_json <span class="hljs-keyword">from</span> demo_json;
  <span class="hljs-comment">-- paseamos el json con apex_json.parse</span>
  apex_json.parse(json_content, l_json);

  dbms_output.put_line ('<span class="hljs-comment">----------------  Array Members  ---------------------------' );</span>
  l_cantidad := apex_json.get_count (p_path=&gt; 'members'  , p_values =&gt; json_content );
  for i in 1 .. l_cantidad
  loop
    dbms_output.put_line ('<span class="hljs-comment">----------------  Objeto: ' ||i||  '---------------------------' );</span>
    dbms_output.put_line ('name: ' ||  apex_json.get_varchar2 (p_path =&gt; 'members[%d].name', p0 =&gt; i, p_values =&gt; json_content) );
    dbms_output.put_line ('age: ' ||  apex_json.get_varchar2 (p_path =&gt; 'members[%d].age', p0 =&gt; i, p_values =&gt; json_content) );
    dbms_output.put_line ('secretIdentity: ' ||  apex_json.get_varchar2 (p_path =&gt; 'members[%d].secretIdentity', p0 =&gt; i, p_values =&gt; json_content) );

      dbms_output.put_line ('................  Poderes ............' );
      <span class="hljs-comment">-- note que aquí usaremos get_t_varchar2 un array de string para almacenar los poderes</span>
      l_members := apex_json.get_t_varchar2 (p_path =&gt; 'members[%d].powers', p0 =&gt; i, p_values =&gt; json_content );
      for i in 1 .. l_members.count
      loop
          dbms_output.put_line ('powers: ' ||  l_members(i) );
      <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

  <span class="hljs-keyword">end</span> <span class="hljs-keyword">loop</span>;

<span class="hljs-keyword">end</span>;
</code></pre>
<p>Y de esta habremos podido acceder a todos los niveles y datos de nuestro objeto JSON.</p>
<p><img src="https://drive.google.com/uc?id=1JrrpoC2dpj6Y-INBjJNXB4-ZkTediXun" alt="run_code_4" /></p>
<h4 id="heading-referencias">Referencias:</h4>
<p>Mas información de este package lo podemos encontrar en:</p>
<ul>
<li><p><a target="_blank" href="https://docs.oracle.com/en/database/oracle/apex/22.1/aeapi/APEX_JSON.html#GUID-11919ED6-CE3D-4497-8733-F56CD27B6BFF">Oracle Doc Ref</a></p>
</li>
<li><p><a target="_blank" href="https://oracle-base.com/articles/misc/apex_json-package-generate-and-parse-json-documents-in-oracle">Oracle Base</a></p>
</li>
<li><p><a target="_blank" href="https://stackoverflow.com/questions/39839701/apex-json-get-varchar2-in-pl-sql">Stack overflow - Acceso a datos específicos</a></p>
</li>
<li><p><a target="_blank" href="https://www.rittmanmead.com/blog/2018/08/parsing-badly-formatted-json-in-oracle-db-with-apex_json/">Otros ejemplos</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Formas de leer un JSON en Oracle - JSON_TABLE]]></title><description><![CDATA[Formas de leer un JSON en Oracle - JSON_TABLE
Para leer un formato tipo JSON dentro de ORACLE existen diferentes formas una de ellas es usando JSON_TABLE.
En el siguiente blog daré algunas formas de leer los diferentes atributos usando PL-SQL.
Tomare...]]></description><link>https://en.aflorestorres.com/formas-de-leer-un-json-en-oracle-jsontable</link><guid isPermaLink="true">https://en.aflorestorres.com/formas-de-leer-un-json-en-oracle-jsontable</guid><category><![CDATA[Oracle]]></category><category><![CDATA[json]]></category><category><![CDATA[REST API]]></category><dc:creator><![CDATA[Angel]]></dc:creator><pubDate>Mon, 13 Mar 2023 04:20:50 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-formas-de-leer-un-json-en-oracle-jsontable">Formas de leer un JSON en Oracle - JSON_TABLE</h1>
<p>Para leer un formato tipo JSON dentro de ORACLE existen diferentes formas una de ellas es usando JSON_TABLE.</p>
<p>En el siguiente blog daré algunas formas de leer los diferentes atributos usando PL-SQL.</p>
<p>Tomaremos de ejemplo el siguiente JSON.</p>
<p>En el script completo dejare toda la demo incluida la creación de la tabla para la prueba.</p>
<p>JSON:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"squadName"</span>: <span class="hljs-string">"Super hero squad"</span>,
  <span class="hljs-attr">"active"</span>   : <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"attributes"</span> : {
                  <span class="hljs-attr">"formed"</span>    : <span class="hljs-number">2016</span>,
                  <span class="hljs-attr">"secretBase"</span>: <span class="hljs-string">"Super tower"</span>,
                  <span class="hljs-attr">"Address"</span>   : {
                                  <span class="hljs-attr">"city"</span>    : <span class="hljs-string">"South San Francisco"</span>,
                                  <span class="hljs-attr">"zipCode"</span> : <span class="hljs-number">99236</span>,
                                  <span class="hljs-attr">"country"</span> : <span class="hljs-string">"United States of America"</span>
                                }
  },
  <span class="hljs-attr">"members"</span>: [
    {
      <span class="hljs-attr">"name"</span>          : <span class="hljs-string">"Molecule Man"</span>,
      <span class="hljs-attr">"age"</span>           : <span class="hljs-number">29</span>,
      <span class="hljs-attr">"secretIdentity"</span>: <span class="hljs-string">"Dan Jukes"</span>,
      <span class="hljs-attr">"powers"</span>        : [<span class="hljs-string">"Radiation resistance"</span>,
                         <span class="hljs-string">"Turning tiny"</span>
                        ]
    },
    {
      <span class="hljs-attr">"name"</span>          : <span class="hljs-string">"Madame Uppercut"</span>,
      <span class="hljs-attr">"age"</span>           : <span class="hljs-number">39</span>,
      <span class="hljs-attr">"secretIdentity"</span>: <span class="hljs-string">"Jane Wilson"</span>,
      <span class="hljs-attr">"powers"</span>        : [<span class="hljs-string">"Million tonne punch"</span>
                        ]
    },
    {
      <span class="hljs-attr">"name"</span>          : <span class="hljs-string">"Eternal Flame"</span>,
      <span class="hljs-attr">"age"</span>           : <span class="hljs-number">1000000</span>,
      <span class="hljs-attr">"secretIdentity"</span>: <span class="hljs-literal">null</span>,
      <span class="hljs-attr">"powers"</span>        : [<span class="hljs-string">"Immortality"</span>,
                          <span class="hljs-string">"Heat Immunity"</span>,
                          <span class="hljs-string">"Inferno"</span>,
                          <span class="hljs-string">"Teleportation"</span>,
                          <span class="hljs-string">"Interdimensional travel"</span>
                        ]
    }
  ]
}
</code></pre>
<h3 id="heading-accediendo-al-nodo-principal">Accediendo al nodo principal</h3>
<p>Para acceder a los atributos primarios deberemos usar el símbolo <strong>$</strong> seguidamente con el nombre del atributo a mostrar.</p>
<p>también podemos mostrar el valor de todo un array u objecto que contiene varios objetos usando <strong>format json</strong></p>
<pre><code class="lang-sql">  <span class="hljs-keyword">select</span> jt.*
    <span class="hljs-keyword">from</span> demo_json a
       , json_table(a.json_column, <span class="hljs-string">'$[*]'</span>
         <span class="hljs-keyword">columns</span> (  squadName       <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.squadName'</span>
                  , active          <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.active'</span>
                  , attributes_json <span class="hljs-keyword">format</span> <span class="hljs-keyword">json</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes'</span>
                  , members_json    <span class="hljs-keyword">format</span> <span class="hljs-keyword">json</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$.members'</span>
                 )
         ) jt;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1HqmEy0sioLASJPlM49yi-gCbMEToweTm" alt="sql_1" /></p>
<h3 id="heading-accediendo-al-nodo-hijo-attributes">Accediendo al nodo hijo <em>attributes</em></h3>
<p>Simplemente concatenamos el objecto a buscar con el atributo.</p>
<p>Para el ejemplo del <strong>address</strong> seria usando <strong>format json</strong>.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">select</span> jt.*
    <span class="hljs-keyword">from</span> demo_json a
       , json_table(a.json_column, <span class="hljs-string">'$[*]'</span>
         <span class="hljs-keyword">columns</span> (  squadName             <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.squadName'</span>
                  , active                <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.active'</span>
                  , attributes_formed     <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.formed'</span>
                  , attributes_secretBase <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.secretBase'</span>
                  , attributes_address    <span class="hljs-keyword">format</span> <span class="hljs-keyword">json</span>   <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.Address'</span>
                 )
         ) jt;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1HwYkZfyylbyF3xBPEPfQRQVP8zUcW_il" alt="sql_2" /></p>
<h3 id="heading-accediendo-al-nodo-hijohijo-attributesaddress">Accediendo al nodo hijo/hijo <em>attributes/address</em></h3>
<p>Continuamos concatenando el valor con puntos.</p>
<p>Note que puede declarar atributos tipo number como formed o zipCode.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">select</span> jt.*
    <span class="hljs-keyword">from</span> demo_json a
       , json_table(a.json_column, <span class="hljs-string">'$[*]'</span>
         <span class="hljs-keyword">columns</span> (  squadName             <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.squadName'</span>
                  , active                <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.active'</span>
                  , attributes_formed     <span class="hljs-built_in">number</span>        <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.formed'</span>
                  , attributes_secretBase <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.secretBase'</span>
                  , attr_address_city     <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.Address.city'</span>
                  , attr_address_zip      <span class="hljs-built_in">number</span>        <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.Address.zipCode'</span>
                  , attr_address_country  <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.attributes.Address.country'</span>
                 )
         ) jt;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1I-r73zpyztqrea1rcQmFsrZ7X5m-WCwc" alt="sql_3" /></p>
<h3 id="heading-accediendo-al-nodo-hijo-members-array">Accediendo al nodo hijo <em>members</em> array</h3>
<p>Como este nodo tiene un array de objectos necesitamos hacer un loop, para ello usaremos <strong>nested path</strong>.</p>
<p>El nodo de powers es igual otro array por lo que mostraremos como <strong>format json</strong></p>
<pre><code class="lang-sql">  <span class="hljs-keyword">select</span> jt.*
    <span class="hljs-keyword">from</span> demo_json a
       , json_table(a.json_column, <span class="hljs-string">'$[*]'</span>
         <span class="hljs-keyword">columns</span> (  squadName       <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.squadName'</span>
                  , active          <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.active'</span>
                  , <span class="hljs-keyword">nested</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$.members[*]'</span>
                    <span class="hljs-keyword">columns</span> (
                        <span class="hljs-keyword">name</span>            <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>)   <span class="hljs-keyword">path</span> <span class="hljs-string">'$.name'</span>
                      , age             <span class="hljs-built_in">number</span>          <span class="hljs-keyword">path</span> <span class="hljs-string">'$.age'</span>
                      , secretIdentity  <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>)   <span class="hljs-keyword">path</span> <span class="hljs-string">'$.secretIdentity'</span>
                      , powers          <span class="hljs-keyword">format</span> <span class="hljs-keyword">json</span>     <span class="hljs-keyword">path</span> <span class="hljs-string">'$.powers'</span>
                    )
            )
         ) jt;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1I1TzgVxN5E8ztio3JFvL1hg2h1W1vzV8" alt="sql_4" /></p>
<h3 id="heading-accediendo-al-nodo-hijo-memberspowers-array">Accediendo al nodo hijo <em>members/powers</em> array</h3>
<p>En el último nodo accederemos de forma similar al anterior usando <strong>nested path</strong> y usando '$'</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">select</span> jt.*
    <span class="hljs-keyword">from</span> demo_json a
       , json_table(a.json_column, <span class="hljs-string">'$[*]'</span>
         <span class="hljs-keyword">columns</span> (  squadName       <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.squadName'</span>
                  , active          <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.active'</span>
                  , <span class="hljs-keyword">nested</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$.members[*]'</span>
                    <span class="hljs-keyword">columns</span> (
                        <span class="hljs-keyword">name</span>            <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>)   <span class="hljs-keyword">path</span> <span class="hljs-string">'$.name'</span>
                      , age             <span class="hljs-built_in">number</span>          <span class="hljs-keyword">path</span> <span class="hljs-string">'$.age'</span>
                      , secretIdentity  <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>)   <span class="hljs-keyword">path</span> <span class="hljs-string">'$.secretIdentity'</span>
                      <span class="hljs-comment">--, powers format json path '$.powers'</span>
                      , <span class="hljs-keyword">nested</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$.powers[*]'</span>
                        <span class="hljs-keyword">columns</span> (
                            <span class="hljs-keyword">power</span>            <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>)   <span class="hljs-keyword">path</span> <span class="hljs-string">'$'</span>
                        )
                    )
            )
         ) jt;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1I3_EBiO3fdxnAw85kSvL4sWYLE_UDegR" alt="sql_5" /></p>
<h2 id="heading-ejemplo-2">Ejemplo 2</h2>
<p>Para este ejemplo cambiaremos un poco la estructura del JSON</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: [
    [
    <span class="hljs-string">"7764355"</span>,
    <span class="hljs-string">"23189291"</span>
    ],
    [
    <span class="hljs-string">"7764355"</span>,
    <span class="hljs-string">"23189295"</span>
    ]
  ],
  <span class="hljs-attr">"meta"</span>: {
    <span class="hljs-attr">"columns"</span>: [
      <span class="hljs-string">"session_id"</span>,
      <span class="hljs-string">"event_id"</span>,
    ],
    <span class="hljs-attr">"count"</span>: <span class="hljs-number">4</span>
  }
}
</code></pre>
<h3 id="heading-nodo-1">Nodo 1</h3>
<p>Para acceder al nodo primario usaremos:</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">select</span> jt.*
    <span class="hljs-keyword">from</span> demo_json_2 a
       , json_table(a.json_column, <span class="hljs-string">'$.data[*]'</span>
         <span class="hljs-keyword">columns</span> (  row_num <span class="hljs-keyword">for</span> <span class="hljs-keyword">ordinality</span>,
                    <span class="hljs-keyword">nested</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$[*]'</span>
                    <span class="hljs-keyword">columns</span> (
                        row_1            <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>)   <span class="hljs-keyword">path</span> <span class="hljs-string">'$'</span>
                    )
            )
         ) jt;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1ICu9vcC_g_4JkCZ2ITPAPhnv1YVjeQ3f" alt="sql_2_1" /></p>
<h3 id="heading-nodos-restantes">Nodos restantes</h3>
<p>Para ello podremos acceder de la siguiente forma:</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">select</span> jt.*
    <span class="hljs-keyword">from</span> demo_json_2 a
       , json_table(a.json_column, <span class="hljs-string">'$.meta[*]'</span>
         <span class="hljs-keyword">columns</span> (  row_num <span class="hljs-keyword">for</span> <span class="hljs-keyword">ordinality</span>,
                    count_c <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">path</span> <span class="hljs-string">'$.count'</span>,
                    <span class="hljs-keyword">nested</span> <span class="hljs-keyword">path</span> <span class="hljs-string">'$.columns[*]'</span>
                    <span class="hljs-keyword">columns</span> (
                        col_1            <span class="hljs-built_in">varchar2</span>(<span class="hljs-number">100</span>)   <span class="hljs-keyword">path</span> <span class="hljs-string">'$'</span>
                    )
            )
         ) jt;
</code></pre>
<p><img src="https://drive.google.com/uc?id=1IE6ry2rEkB47yNtKFYM6nKMgQuuUDyWh" alt="sql_2_2" /></p>
<h4 id="heading-referencias">Referencias</h4>
<p><a target="_blank" href="https://docs.oracle.com/database/121/SQLRF/functions092.htm#SQLRF56973">Oracle Doc Ref</a></p>
<p><a target="_blank" href="https://forums.oracle.com/ords/apexds/post/extract-json-from-clob-4652">Ref_2</a></p>
]]></content:encoded></item></channel></rss>