Has this ever happened to you?
You’ve built up a beautiful set of pages, taking your user through a flow that the business mapped out for you after really thinking everything through. Just kidding, most likely they handed over a napkin with some scribbles on it, and said: This is what we want.
You go away, put your blood, sweat and tears into it, and finally present your masterpiece, complete with lovely APEX Wizard Progress List. They love it.
They love it, but have one small request. One. Tiny. Thing.
Let’s please move the 4th page over to the 6th, 6th becomes 4th. And actually, page 5? I don’t think we actually need it anymore. And come to think of it, move this page here and this one here. It’s just switching up the order a bit, that’s a quick fix, right?
And your mind immediately goes to all the NEXT and PREVIOUS buttons you’ve set up in your flow, your page branches, that you now have to go basically re-do entirely. Not to mention re-ordering the Wizard List you’ve created for your flow.
Let’s just say this happening to me once on a project a few years ago made me think THERE MUST BE A BETTER WAY. Because you know me, I am all about doing things faster.
So ever since then, I never go in to an APEX wizard development without making sure we are set up to make this fully flexible and dynamic.
Don’t fret! You’ll be able to download our demo code and app at the bottom of this post
Global Application Items
The first thing you’ll need to do is create 3 global application items: G_NEXT_PAGE, G_PREVIOUS_PAGE and G_WIZARD_CODE
Table to support dynamic APEX Wizard
This is where the magic happens. Because we’ll store our navigation logic in a database table, we’ll be able to modify our navigation on the fly!
WIZARD_CODE: This is simply an identifier for your wizard. We include this because eventually, your application might include multiple wizards, and we need to know which pages belong to each wizard or workflow.
SEQ_NO and APP_PAGE_ID: These columns help us identify what position (sequence) each page occupies in the flow.
LABEL: This will be the label or identifier of this particular step in the process. It will appear in your Wizard List.
Initial data might look this, for a wizard that is supposed to take the user from page 1, to 2, to 3, to 4, to 5 and finally 6.
Please note that I have learned from experience to allow ‘padding’ in my numbering, hence the gap of 10 each time. You just know that the stakeholders will not only want to change the order around, but also add and remove pages. Trust me on this one.
Package to support dynamic navigation
We now need to create a package to support our dynamic navigation. At it’s simplest, we need functions to return both NEXT_PAGE and PREVIOUS_PAGE every time our users are on a page included in a workflow.
Our package spec, in its simplest form, looks like this:
Basically, when passing in an app_page_id and wizard_code to my functions, I can determine both next and previous pages by using the SEQ_NO in my table.
The next step is to create an application process that will populate our global application items G_NEXT_PAGE and G_PREVIOUS_PAGE.
We create the following Before Header Application Process:
We also include a condition on this, because we do not want it to execute for every page, but rather for any pages that, according to our WIZARD_NAVIGATION table, are part of the wizard:
Page Branches and Buttons
Every one of our pages involved in this flow should include NEXT and PREVIOUS buttons, that submit our page and then redirect the user to either G_NEXT_PAGE or G_PREVIOUS_PAGE, accordingly. Here, as an example, is our ‘NEXT PAGE’ branch.
By using our global applications items :G_NEXT_PAGE and :G_PREVIOUS_PAGE, that get recalculated on every page load, we are significantly reducing time spent, and making our APEX Wizard nice and flexible.
Don’t forget to put conditions on your NEXT and PREVIOUS buttons. Our 2 functions are designed to return -1 if a PREVIOUS or NEXT page is not found (ie: if we are sitting on the first or last page in the wizard).
We therefore simply use: nvl(:G_NEXT_PAGE,-1)!=-1 or nvl(:G_PREVIOUS_PAGE,-1)!=-1 as conditions on each of our buttons.
Dynamic List and List Region
We also need to create a Dynamic List in Shared Components. We simply called ours Dynamic Wizard, and we can use it in any of the APEX Wizards we create in our application.
It looks like this:
Super simple, right?
Add the wizard list to your pages
Finally, we need to add our Dynamic Wizard list as a Wizard Progress region to each of the pages in our wizard.
Don’t forget you can use the ‘Copy to Other Page’ functionality in Page Designer to do this faster!
One last missing piece is actually setting the value of :G_WIZARD_CODE. We did this simply in a before header page process of our first page, but you can use your own logic or set things through a link, etc.
Ok, now you’re ready to have some fun! Run your page, and you should already be in pretty good shape at this point: