Skip to Main Content
Feature Request FR-2896
Product Area APIs
Status CLOSED

39 Voters

A drop-in replacement for the HTP package for use with new Dynamic Content region

morten Public
· Dec 19 2022

Idea Summary
The new “Dynamic Content” region requires developers to rewrite existing code if they want to move to the new, refreshable region type from the existing “PL/SQL Dynamic Content”. A drop-in replacement package, tentatively called APEX_HTP, should be offered as a drop-in replacement for HTP.

Use Case
For the million instances of the existing PL/SQL Dynamic Content regions out there.

Preferred Solution (Optional)
See my blog post at https://ora-00001.blogspot.com/2022/12/dynamic-content-region-in-oracle-apex.html

We reviewed this idea carefully, and while it was interesting, we concluded that it is unlikely to make its way into APEX in the foreseeable future.

Comments

Comments

  • jochen.zehe OP 2.8 years ago

    You got my vote here!

  • steven.feuerstein OP 2.8 years ago

    Definitely consistent with the spirit and usual approach of the APEX team.

  • olafur tryggvason OP 2.8 years ago (edited 2.8 years ago)

    I created this function around 5 years ago to grab the results from my old HTP procedures. Works well, APEX team could do a simple wrapper that does this.

    declare
      nm              owa.vc_arr;
      vl              owa.vc_arr;
      l_webpage       htp.htbuf_arr;
      l_lines         pls_integer := 9000000;
      l_result        clob;
      l_header_done   boolean := false;
    begin
      /* Initialize the OWA env */
      nm (1)   := 'SERVER_PORT';
      vl (1)   := '443';
      owa.init_cgi_env (nm.count, nm, vl);
      /* Run any htp procedures */
      my_fine_proc;
      /* Get the output */
      owa.get_page (l_webpage, l_lines);
      dbms_lob.createtemporary (l_result, true);
      for i in 1 .. l_lines loop
         if l_header_done = false and instr (l_webpage (i), chr (10) || chr (10)) > 0 then
            -- Taka burt request header
            l_webpage (i)   := substr (l_webpage (i), instr (l_webpage (i), chr (10) || chr (10)) + 2, length (l_webpage (i)));
            l_header_done   := true;
         end if;
         dbms_lob.writeappend (l_result, length (l_webpage (i)), l_webpage (i));
      end loop;
      
      return l_result;
    end;
    
  • nicolas pilot OP 2.7 years ago

    Why not just add an option on the new “Dynamic Content” region for this ?

    So the region can behave like the “PL/SQL Dynamic Content” region or like the new one.

    So that we don't have to modify anything in our app.

  • morten OP 2.7 years ago

    @pilot.nicolas That's a good point. There could be a Mode attributte, “Classic (output via htp.p)” and “Extended (function returning clob)”. Although I guess there would be some usability issues / confusion when developers don't get the region to refresh if it's in the wrong mode, etc.

  • nicolas pilot OP 2.7 years ago

    Ideally, refresh should work in both modes…

  • morten OP 2.7 years ago

    @pilot.nicolas But adding support for refreshable regions was the whole point of introducing the new region type, and the reason why they “had to” change the way you add content. See my blog post for details.

  • connor.s.mcdonald APEX Team OP 4 weeks ago

    A suggestion for alternative solution to your proposal (to ease the burden on devs).

    If you call (as per your blog post example) “apex_htp.init” then we “know” that we want to be staging output in a clob, as opposed to directly returning it.  So (if htp/htf were augmented to detect this), then the code changes would be negligible, ie

    Original  

    htp.p('blah');
    htp.p('blah');
    htp.p('blah');
    htp.p('blah');
    

    Replacement

    apex_htp.init;
    htp.p('blah');
    htp.p('blah');
    htp.p('blah');
    htp.p('blah');
    return apex_htp.get_clob;
    

    because the presence of the init call, means that htp/htf et al would know that we are now in a region-returning-clob as opposed to the legacy region type.

    Note: This is not a statement of “_yep, we're working on this and this is how we'll do it_”

  • jayson hanes Admin OP 4 weeks ago

    In an effort to manage expectations, this idea has been moved to a “Not Now” status, which is the reality of this idea from the APEX Team currently. Note that closed/Not Now ideas ARE NOT destined to be never implemented.

  • denis.savenko APEX Team OP 4 weeks ago

    As a piece of feedback and a possible alternative approach “for now”, APEX_STRING.PUSH in combination with APEX_STRING.JOIN_CLOB could substitute the HTP.P calls.

    Using the example from the blog post:

    declare 
      l_buffer apex_t_varchar2;
    begin
      apex_string.push(l_buffer, '<h4>List of employees:</h4>');
      apex_string.push(l_buffer, '<ul>');
      
      for l_emp in (select ename, job from emp order by ename) loop
        apex_string.push( l_buffer, apex_string.format( '<li><strong>%s</strong> (%s)</li>', apex_escape.html(l_emp.name), apex_escape.html(l_emp.job) ) );
      end loop;
      apex_string.push(l_buffer, '</ul>');
      
      return apex_string.join_clob(l_buffer);
    end;
    

    Where the changed parts are:

    1. Added declaration of an apex_t_varchar2 buffer variable at the beginning.
    2. Replaced all htp.p( with apex_string.push(l_buffer.
    3. Added return apex_string.join_clob(l_buffer); at the end of the PL/SQL block.

    These are the steps that are similar to invoking apex_htp.init, apex_htp.p and apex_htp.get_clob, proposed in the post.

    Note: This is an idea of how to preserve the code style, similar to htp.p calls, using the existing APIs, but this is of course not a drop-in replacement.

  • morten OP 4 weeks ago

    @denis.savenko Did you read my blog post from 3 years ago?

    Your suggestion fixes only one of the 4 problems (you don't need to do concatenation).

    • You have to declare a local variable
    • You have to clutter the code with concatenation
    • You cannot split your code into subroutines unless you pass around your local variable holding the final result 
    • You have to rewrite every piece of existing code (if you want to use the new region type)

    Frankly, one is better off just making one's own replacement package and using that, like the quick prototype I posted back in 2022:

    https://github.com/mortenbra/plsql-sample-code/blob/main/xtp.pkb