I was experimenting lately with page items and how to setup APEX page (preprocessing section) in more cleaner, reusable and generic way. I also wanted to move the logic from APEX to PL/SQL packages to fulfil my MVC vision (to keep as much as possible code in PL/SQL, not in APEX). So I made this up.
For every page in application I have created a package, "P0" for page zero, "P100" for page 100... For 100 pages I would create 100 packages, but that is still better then have the logic scattered on individual pages. And most of the times I have to create packages anyway because I need a place where to store form/grid handlers and page related logic. Then I have created INIT_DEFAULTS procedures in each of these packages. And in this procedure I setup page items and run the pre-rendering processes. Since we are talking about packages, I can reuse other procedures and function and limit the amount of copy pasted code.
I use my wrapper around these session state calls (so I can check if these items exists for example), but the typical INIT_BLOCK on non zero pages would look like this:
PROCEDURE init_defaults /* in P# package(s) */ AS BEGIN -- logger NULL; -- call page related processes NULL; -- set page items APEX_UTIL.SET_SESSION_STATE('P100_ITEM1_NAME', 'ITEM1_VALUE', FALSE); APEX_UTIL.SET_SESSION_STATE('P100_ITEM2_NAME', 'ITEM2_VALUE', FALSE); EXCEPTION WHEN OTHERS THEN -- logger RAISE; END;
I was tired of creating INIT_DEFAULT process on every APEX page and referencing correct procedure there. Of course there is a better way. So create INIT_DEFAULTS procedure in P0 package (since I store other shared things there). The code checks if there is a procedure in matching package and if yes, then it will execute this:
PROCEDURE init_defaults /* in P0 package / AS v_package_name user_procedures.object_name%TYPE; v_procedure_name user_procedures.procedure_name%TYPE; BEGIN -- initialize page 0 items (if needed) NULL; -- find init block for specific/current page BEGIN SELECT p.object_name, p.procedure_name INTO v_package_name, v_procedure_name FROM user_procedures p WHERE p.object_name = 'P' || TO_CHAR(APEX_APPLICATION.G_FLOW_STEP_ID) AND p.procedure_name = 'INIT_DEFAULTS'; -- IF v_procedure_name IS NOT NULL THEN EXECUTE IMMEDIATE 'BEGIN ' || v_package_name || '.' || v_procedure_name || '(); END;'; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; EXCEPTION WHEN OTHERS THEN -- logger RAISE; END;
So now I don't need to create any preprocessing on these 100 pages, I just need to put this into new application process INIT_DEFAULTS and run it on load before headers:
Done. What do you think?
One additional note, I also have views starting with "P#" so when I look at the objects in schema, I immediately know that these views are used on that page. And this works great for me.