Showing posts with label APEX. Show all posts
Showing posts with label APEX. Show all posts

Wednesday, 15 October 2025

Ideas as a precursor to Oracle APEX #JoelKallmanDay

 It seems if I post only once a year, it's for Joel Kallman Day...

Last year I hinted of a small side story to a remediation project that was upgrade the entire stack supporting a (mostly) Oracle Forms application.

Along the way I also had to remediate a little one-Form tool Connor McDonald build for developers, and it had some features of some note.

It should be stated this was written at a time when flip phones were all the rage - and notably before Oracle APEX gave birth to Interactive Reports.

I won't include a screenshot (for privacy concerns), but it's basically a multi-row data block allowing queries and edits of selected records - an Editable Grid, back in the heyday of Forms.

Hmm, let's see a list of default display options, plus the ability to save my own current filter - which stores the values set in Enter-Query mode - remember that?!

Saved Reports

And let's see an ability to re-order the columns, and or even hide specific columns.

Re-order columns

Yes, Connor created a customisable editable grid, in Oracle Forms, a few years before smartphones were a thing.

Editable Grid in APEX

All these highlighted features that come straight of the Oracle APEX box.

An example of great ideas that pre-date the technology that can better support them.

Or is it the ideas that drive the development of better technology?

Or perhaps a bit of both...



Wednesday, 16 October 2024

Upgrading Forms compared to APEX #JoelKallmanDay

My current project has involved rebuilding the entire aging stack for an environment that's currently 95% Forms, with any new work developed in Oracle APEX. The upgrade covers all layers -  from OS, to DB, to browser, and to application builders - all heading to contemporary versions.

The general directive is like-for-like, so the applications should work the way they used to, if not a little better.

So to dumb down the process, we basically need to recompile everything on the new stack, fix the gaps, then test extensively.

***

It's been an interesting project to compare the type of effort required to uplift a large Forms application compared with a modest set of Oracle APEX applications. I do this with the awareness that it's like comparing apples and toaster ovens, but the outcomes have been interesting enough (to me, at least), that it's forming the basis of my next presentation.

In fairness, there wasn't too much in regard to general syntax used within Forms that needed addressing. Nothing has significantly changed in Forms functionality, and we just needed the app to function as it did previously. 

There were a few environment level settings we found to help with some backwards compatibility, thereby saving some rewrite. 

From an infrastructure perspective, however, there were a number of aspects we needed to tackle to keep up with the modernisation of the stack. This included the launcher (Applet->FSAL), the SSO mechanism, document (blob) handling, and guiding the application to a tidy close after a timeout occurred. Compared to Oracle APEX in a modern ecosystem, the sting of that last item will linger.

***

This application has a considerable amount of blob content stored in (now deprecated) OrdImage data types, so that one item took up a considerable portion of effort - from shifting the data; to modifying the UI to load and view the attachments.

Oracle APEX came to the rescue for this case, replacing an old mod_plsql interface that supplemented a Form, and an ancient looking webutil UI in another.

The saddest part of the Forms upgrade was the amount of time I needed to spend ensuring session timeout didn't get the user stuck in a dead session. Part of me kept thinking how much of that 95% we could have started to shift to APEX with that time... 

***

As for the APEX applications, we took advantage of the testing that was going to happen and updated the Universal Theme (UT) of the applications.

Prompt to upgrade the APEX UT

I'm working through the issues that came out of this for the presentation, but most of the issues we needed to remediate were due to the UT update - not the upgrade of the database and the Oracle APEX version. And even then, most of the issues are relatively benign UI differences which don't need attention. A good handful of which were on purpose, for the benefit of APEX users - just against the fine grain of "like for like".

We could have upgraded both the database and the APEX version, left the UT alone, and only had a few minor version related adjustments to take care of - then still have a nice new bag of tricks to explore.

This is a far cry from earlier days of APEX where a big hurdle to upgrades was contemplating the significate cost of migrating to a newer theme with improved considerations.

But again, this wasn't necessary. I recall Joel presenting a session where he took an APEX application built some time ago, spanning a gap of multiple APEX versions. He just imported the application, and it ran without change - even when APEX was still a comparative youngster.

This methodology and mindset hasn't changed. I've done a few APEX upgrades in my time, and my recollection of any issues were related to heavy customisation - rarely native usage. Any native changes were always well documented, with a guide to transition to what is an improved outcome.

***

So today I share this heavily abbreviated and filtered anecdote of my recent explorations with Oracle technology, and once again remember Joel as a guiding force to help transform Oracle APEX into a development tool that has longevity without heavy maintenance, while still making the developer's life easier each release. 

And also a feature haven for those Forms developers out there.

I can even draft next year's post with another small side story from this upgrade project, reaching deeper into the past of Oracle APEX.

Wednesday, 11 October 2023

Pressing Pause on APEX #JoelKallmanDay

To honour this year's #JoelKallmanDay, I'd like to use this as an opportunity to speak about mental health, rather than get technical like I have done in the past.

Yes, my blog posts have been particularly lean - this is my first post since the same event in 2021 - I completely missed last year. Not just this special event, but from any posts whatsoever.

It's not that I've moved away from Oracle, nor that I'm short on inspiration - I'm still emailing myself ideas on a regular basis, but I haven't been setting time aside to transform them into something to post.

And I haven't been totally silent - I did update my Presentations page after presenting at a couple of local meetups this year. And you'll still find me on the Oracle and Stack Overflow forums.

There have been a few reasons for the slow-down in activity, but in part this has been due to my front-end development time being far less than it has been in the past. The Forms->APEX migration project I joined has been paused, and in the meantime I find myself on a project remediating the environment to first upgrade the stack from fax machines and teletype.... well, things aren't quite that old, but there's no point giving the car a flash new paint job without first replacing the tired old engine, right? 

But when you're into painting, mechanical work is far less exciting, and it can take a toll on how one feels about their job.

I still get to paint the odd fender, but that's only part-time.

Sunset after Eclipse 2023

It can't always be about rainbows and lollipops, but after many years of working in the candy factory at the end of a rainbow, I have to wait patiently for the new factory to open under a new rainbow.

Perhaps that's enough mixed-metaphors for now. I'm still here, still passionate about Oracle and the APEX product, and I have exciting times in my relatively near future.

It's also not the first time I've had setbacks of sorts. There have been significant development works that ended up being permanently parked. These were amazing builds that I had fun being involved with, but for one reason or another they either never ended up in the user's laps, or didn't stay there for long. However, besides feeling like my time was wasted, there wasn't really a direct impact on me.

Stepping away from the front-end and spending far more time in the back-end - or even finding myself updating some Oracle Forms and tweaking their configuration, really makes me appreciate what I've found in the highly productive world of Oracle APEX development.

In the meantime, I find community events like this, and perhaps also the first local conference in some time next month will invigorate me enough to take the time to revive my blogging vibe, and maybe a side project or two that haven't gone completely dormant.

To the reader: how have you handled lulls in your career? Have you found periods of time that, while perhaps still being technically interesting, haven't completely satiated the programmer's need to create? What did you do to quench your thirst?

Sunday, 7 November 2021

APEX in tense. #JoelKallmanDay

Now I feel extremely luck to live in a part of the world that has been relatively free of impact from COVID-19, certainly from a societal health perspective.

That said, I'm fairly certain there has been an indirect impact on my non-billable APEX activities - one only needs to look at my blog history over the past few years. Blogging is an outlet I've always enjoyed, and I miss doing regularly. 'Next week' I'll start doing it more regularly. Next week...

Blog frequency dips

But I was flicking through some tweets and saw Tim's announcement regarding #JoelKallmanDay, an initiative extending from past appreciation days, but now honouring the late great Joel Kallman, who sadly succumbed to COVID-19.

While I may be late to the event, I still feel compelled to contribute in some form. I only had the honour of meeting Joel a few times, but I know he's been an influence in my outreach. And I certainly feel his passion through the extended Oracle APEX team, whom I've also shared some wonderful moments with. I think about him frequently.

Like Tim, I thought I'd do an abridged history of my path to APEX. I had my first exposure to generating HTML from PL/SQL back in University, though too late in my final year project to do anything exciting with it - but I felt back then it seemed quite a powerful concept.

That said, I ended up cutting my programming teeth with Oracle Forms for a good number of years before my employer in ~2008 asked/directed/suggested I learn Oracle APEX while it was v3.2.

I was really given the opportunity at that point to jump in the deep end with APEX and explore what could really be done when it comes to designing applications that help the user get the job done. Not only that, I started the conferencing thing and got exposure to people like me from all over the world.

It wasn't long before I started training other people to use Oracle APEX, and if nothing else, that certainly helps you learn the ins, outs, and shortcomings of your development tool. Combined with regular involvment in the online forums, where even answering a question I thought I knew often comes with other people contributing their own perspectives, offering an enhanced method, or other consideration.

I've since had the chance to speak at two Kscopes, where I met Joel and quite a few people in the APEX team. They're all so passionate about the product, and offer their own special skills to help mature the product in the right direction - adding features faster than I can learn them. Not only that, they all know how to have fun outside the technology world. Something particularly important for our mental health in these challenging times.

I've now been on a few projects where we're transitioning Forms application to the web-based world that APEX lives, and now I have the pleasure of helping convert a large Forms application I had a decent part in developing - back before I knew what APEX was.

For me this is a particularly exciting time. It was somewhat of a plunge to leave my previous job during these testing times, but it's opened a raft of new opportunities - all the while I wish I had more time to tinker with my own little APEX side projects. 

Monday, 26 April 2021

Kicking the tyres on Oracle APEX 21.1

 It's lockdown on a long weekend, and Oracle APEX 21.1 has just been deployed to apex.oracle.com, how could I not have a play?

We've been provided a link to New Features, but for some aspects I'll need some help from the documentation, when it becomes available - and perhaps some examples from people more knowledgable than I.

Native Map

This will be one of those features where I'll need some more guidance, and I suspect this AskTom Office Hours session that I haven't watched yet would probably be a good start.

The create page wizard looks promising

Create native map

And after fiddling around with the region attributes, I was able to plot three lat/long addresses onto a map.

Native map runtime

I also noticed there were some native Dynamic Actions associated with the map region, but I'll patiently await further information on this one. 

Good to see it's dark-mode ready, though. 

Lazy Loading

This is one feature I've been waiting to become native, and I probably would have been a little disappointed if it didn't arrive with 21.1, and here it is, nice and simple.

Lazy loading option

We can emphasise the behaviour with the browser's device toolbar options, for instance, throttling to 'Low-end mobile'.

Here I have an IR and Classic Report both using the lazy loading option.

Lazy loading runtime

Custom built solutions can use the no-data-found message to inform the users data is still loading, I would like this feature to offer some facility in that regard.

Code Editor

I like it when we're giving options on what code editor features are available, and it looks like we've been provided a few more keys to the playground with the 'Monaco' based code editor.

Code editor settings

There are some APEX specific improvements, too. For instance, we're now notified when a page item doesn't exist - thought I think it needs a bit more polish. It appears to be case sensitive, and doesn't clean up very well once you've corrected the problem.

Code editor item reference check

It seems we have a few more keyboard shortcuts available now for case transformations. That's always nice.

Universal Theme enhancements

If you like the Redwood Light theme, it seems that's been given more love compared to the first round.

There are some added declarative options, but I'll probably need more context as to how they're used. I don't think the UT application has those guidelines yet, or I didn't see them.

Row CSS classes seems like it should have been there all along with Column CSS classes. What would be an example of it's use?

Grid layout property addition

CSS classes against the Theme Style seems like it will allow more elegance, but again, I'd like to see an example.

Theme style property addition

Having even a cursory look at these new features also made me consider how I've been utilising existing behaviours. The File URL for a theme style seems a better spot than I've been using for custom CSS I've added to fill gaps in the dark mode.

Template Options have always had a few surprises in version upgrades, though I'm not sure what the region Heading Level is supposed to do.

Region template options

Universal Theme application

I notice the UT app has some extra options in the navigation bar.

The version facility sounds like a fair addition for those who may be a version or so behind. I wonder what this changes, exactly. Does it affect content, or just the styling that's applied. Though I notice you can't select an older theme while dark mode style is applied.

UT app navbar

The Style option is pretty cool. I've been playing with a similar feature in my app, but it seems like I've been submitting the page like a chump - I'm curious as to how this work.

Date Picker

It's always good to get improvements on such a common component. The Maximum Item seems like a more declarative facility compared to what we have now, but now it appears to be dynamic - responding to a related item's selection. Perfect for from/to selections.

New date picker settings

Display Inline really stands out, must be for when date selection is rather important.

Now that DanielH is on the team, it almost seemed inevitable that the time picker would be enhanced in some form. This could mean another plugin is no longer required, though I'll reserve judgement until after I play some more.

Time picker enhancement

Import export as ZIP

This seemed inevitable, but I don't think it will impact me so much.

Application Data Loading

I need to spend time to revisit this feature. The column mapping aspect intrigues me.

Faceted Search enhancements.

I haven't used this facility in production yet, but it's good to see it progress as it's such a ubiquitous web feature.

REST Data sources

Schedule frequent loading of CSV data from the web? Getting data into the database easier has gotta be good.
Juergen has a good way of putting this:
"Put your Excel Sheet in your Sharepoint, change it as you like and I give you an real time App on top of it" feature

Markdown upgrades

This will float some people's boat.

Miscellaneous

I like the sound of client side numeric formatting, and improved client side validation.

Conclusion

This release seems in line with the stated roadmap, though as Andre describes, there are also some long standing painpoints that exist for the developer that could use some attention at some point.

The Oracle APEX development team have had some quality additions recently, though the whole COVID situation has potentially slowed some general improvements. 

That said, the product continues to improve, and the team responds well to community feedback. I also look forward to seeing improvements in the feedback application itself.

Get in there, kick the tyres, and give the development team some constructive feedback. We can all help make this an even better product.

Hold the phone! 

Just as I was about to clean the post up for submission, I saw Juergen's tweet about client side conditions on individual dynamic actions. I think the team are underselling that feature on the New Features list!

Client side condition in DA actions

Though as handy as this feature will be, I suspect it could be abused - in regard to maintainability. It could leave to code/behaviour that's a little difficult to follow. We'll have to keep an eye on that.




Thursday, 21 January 2021

2020 Blog Review

Ahem.

select apex_string.format(q'[It's been %s days since my last confession]' 
                         ,trunc(Sysdate) - date '2020-04-14'
) "G'day World" from dual
G'day World
-----------
It's been 282 days since my last confession

It's been so long, the Blogger UI has changed. I don't think I like it. Maybe another thing to consider adjusting in the new year...

It's also been four years since my last blog review, and I promised this one may be a doozy.

2020 was something else, right? I spent a good portion being thankful that I 

  1. kept my job
  2. could work from home
  3. live in such a remote city, which helped keep my family & friends safe

Aside from the APEX@Home event in April, I decided to scale my extra-curricular work right down, hence the lack of blog activity over the past year. I've kept active in the forums, because looking through those helps give my brain a bit of a reset when focus starts wandering.

But I wanted to be sure I had the time, energy, or whatever it was I needed to power through whatever 2020 potentially dished out.

Thankfully, from my end of the world, things are relatively OK. I've been to a few outdoor gigs, and aside from a few regular adjustments, life is kind of normal. Kind of. We're not completely insulated from the outside world, and things could change in a moment.However, I've used this down time to improve my investment game, should my IT career start to falter.

But that leads onto another astonishment for the year. I saw a lot of people I knew outside tech lose their jobs, though thankfully most of them had options elsewhere. Meanwhile, I'm presented with numerous avenues. I happened to choose a new avenue.

As of the end of January 2021, my tenure at Sage Computing Services comes to an end after 13 years. 

When I look back, it's staggering what I've done with the support of Penny at Sage.

  • Transitioned from Forms based development to Oracle APEX.
  • I published a video series using Oracle APEX. 
  • I published a book involving Oracle APEX. 
  • I was recognised as an Oracle ACE.
  • I spoke Kscope. Twice. Well, two cities, 5-6 sessions, I think.
  • Helped create some amazing project work with an amazing team.
  • Found a great work-life balance.

And I still have the yearn for more.  I have two more ideas for video series I still want to explore, and one could work as another book.  

It was a really tough choice leaving Sage, but yet again, Penny supported me through the options, and I'm fairly sure I've found a project that's the perfect fit for me, and it was hard to ignore.

And for a moment there I felt like I was presented with the cliche of red vs blue pill.

Development or ...

I'm not sure which one represents the world of development, but I didn't identify a managerial style job back at University - so while I 'manage' a lot of things as a developer, my passion is cutting code.

Therefore, the coming years are going to be explored as a contractor at a new firm, while I return to a past client to transition them from Forms to Oracle APEX. The good & bad news: I built a bunch of those Forms!

2020 project challenges were tough. I'd be kidding myself if I didn't think 2021 projects at a new location will be any different, but I'll be looking to step up my skills & habits while I'm at it.

Hopefully you'll be hearing more from me via this medium in 2021.
I've missed it, and I'm ready to come back.

And here's to seeing James Webb Space Telescope finally launch this year!

Tuesday, 14 April 2020

Oracle APEX @ Home

The world has obviously lost a number of in-person conferences in recent times, but luckily we have people like Martin who asked the APEX community to jump, and they responded "how high?"

And out comes 24 hours of 24 speakers with APEX @ Home, on April 16th 2020. A lot of regular speakers appear to have pulled out some awesome sessions, and then there's my dinky one on APEX logic suggestions.


It's an AskTOM Office Hours session, so it's also recorded for later viewing, but you'll miss the chance for live Q&A.

And you'll also miss the challenge Connor put out there - to get a virtual selfie with all 24 speakers. Good luck with that.

What I've wanted to do, but haven't had the time, is roughly plot the time zone differences for each speaker. I'm talking at 8pm, but I bet it's all over the shop for every speaker, at various times of the day!

Sunday, 15 March 2020

Inspect Element Deep Dive, with Oracle APEX

In my previous post about tweaking APEX classic reports, most of the settings were "low-code" configurations. All except the final line of JavaScript that moved the radio group from its default location, to the region's title bar where we'd normally find buttons.

But how did I conjure that statement?

When I wrote my book on jQuery in APEX, my general premise was each web page can be queried like a database can, you've just got to learn how to apply the filters to find the relevant component.

Browser Tools


In most browsers, if you right-click on any component on the page, then choose 'Inspect Element', you'll see a window appear showing you what the web page looks like prior to markup.

I can also use F12 in Chrome on my laptop to bring up this console window, but don't presume that's the same anywhere - I borrowed a laptop for a webinar once, and it put the laptop in aeroplane mode. I digress... you can dock this console window to the bottom, side, or have it floating elsewhere.

Where is my item?

If I inspect the radio group, it will take me to the specific radio option, in this case 'Accounting'.

As I move the mouse up the tree, different portions of the page will highlight, signifying exactly what part of the page the HTML represents. Sometimes you'll also see orange & green, signifying margin & padding respectively.

Find the relevant component on the page with help from highlighting

I kept going until I found the P29_DEPT_Z_CONTAINER, which holds all of the contents of my radio group item P29_DEPT_Z. The id property allows us to 'query' this page using the filter (selector) #P29_DEPT_Z_CONTAINER.

I can wrap this selector with the $() function to return that portion of the page, a portion I would like to move somewhere else.

You can also see what happens when you enter this in the console window - it returns a result.
$('#P29_DEPT_Z_CONTAINER')

What function do I use?


I use this jQuery cheatsheet to help identify the relevant command I need. Sometimes there's alternates depending on what expression is on either side of the equation.

In this case, I want to take my radio group, and append it to somewhere else on the page.
$('the object I want to move').appendTo('where it is going');

Where is it going?


The next step is to identify the part of the page where my snippet of HTML will be going.

I can use the same inspect element technique to locate a region already dedicated to placing buttons.

Find the destination using the same technique

Here I've located my zhuzh region by the Static ID I've applied (purple), and there is a div the represents the location of the buttons. I can identify this part using one of the classes (red)

Classes are prefixed with a dot when used within selectors
.t-Region-headerItems--buttons

You need to take care when determining which selector to use. IDs should be unique (but aren't necessarily), and classes certainly aren't unique.

A pairing of ID and class is often an effective combination, but you need choose the right class. One you've added your own is often safe, and you can inject classes into the page using various APEX attributes. They can be anything.

This t-Region set of classes is defined within the Universal Theme. One early problem with APEX was these classes would change from theme to theme, as we didn't have a universal theme, so migrating custom code could be awkward.

The UT has changed some classes over time, but documented ones should remain constant.

Verifying the selector


Using the find function in the Elements tab allows you to test your selector, test how many results are returned on the page. If I just use the class, I actually get two results - one for each standard region I have on the page.

Count components on the page by searching in element tab

Combining these selectors will ensure I only get the button class for the relevant region. This is what forms my destination selector in the appendTo().

Testing the move


We can test the final command in the Console tab of the browser tools.

$('#P29_DEPT_Z_CONTAINER').appendTo('#zhuzh .t-Region-headerItems--buttons')

If I test this without using the #id selector, then the radio HTML will be appended to both regions.

Test your command within the runtime page

Once you're happy with the result, you can add it to the relevant portion of your page. In this case, I put it in the page attributes 'Execute on Page Load', though often it's within a dynamic action.

Playing with CSS


While we're here, it would be remiss of me to mention how you can play with the CSS attributes in the browser tools.

Play with styling on the rendered page

In this example I found the background colour property of the radio group label, and turned it green.

This was done without re-rendering the page, but it's only relevant until I do re-render the page, just like when we applied the console command to move the radio group.

But it's a great way to test/tweak CSS commands to see the result before applying it as CSS content rendered with the rest of your page.

The syntax would be similar, we would take a selector, but in the same syntax as what's found in the Styles sub-tab. You can copy/paste from the tab if you're not sure.

.t-Region-headerItems--title {color : red}

This CSS in the page would apply the styling during render of all page markup, but we could also do this on demand with jQuery, with just a slight adjustment to the syntax - and this is but one variation.
$('.t-Region-headerItems--title').css('color', 'red');

Conclusion


These concepts can arm you with some nifty behaviours, above and beyond what comes with APEX.

Or to think of it a different way, if can really empower your use of dynamic actions, as the same instruments can be wielded as a jQuery selector, dynamic action condition, part of set value action, to name a few.

Doing this with an Interactive Report also provided extra challenges.

See a video on how to action this blog here, and the app here.

Happy APEXing!

Tweaking Classic Reports

I like classic reports in Oracle APEX.

They're so versatile, and while it may not look it in this example, adjusting particular declarative settings can make a real difference in a small region displaying pivotal data.

And this still looks like a report. You should see what else they can do.

Left region is default settings, right region has a few options set

Base Behaviour

The classic report is straight out of the wizard, this example performs a cross join to inflate the data set.
select e.* from emp e cross join emp e2
where :P29_DEPT is null or e.deptno = :P29_DEPT


A where clause has been added to filter by Dept, if supplied.

Page Items to Submit

Note one of the most important properties in APEX - Page Items to Submit, in this case nominating the P29_DEPT item.

There is a Dynamic Action defined on Even Change for the item, which simply refreshes the nominated region.

Simple Dynamic Action

If we don't set Page Items to Submit on the region, then the database won't know about the change made on the browser. Any item specified in this list upon refresh will have the current value set in the browser sent to APEX session state (a key/value table in the database), so when the query binds the value, the database knows what the browser knows.

So when I select a department, the employee list refreshes to show the relevant department.

Make Left look like Right

This may seem like a long list, but it doesn't take long at all once you know where to click. I estimate 23 clicks, as of 19.2.
  1. Modify Region properties
    1. Change Template Options for region
      1. Tick Remove Body Padding
      2. Tick Show Region Icon
    2. Add Icon: fa-list
    3. Add Static ID: zhuzh
  2. Modify Report properties
    1. Change Template Options for report
      1. Tick Stretch Report
      2. Report Border Horizontal Only
    2. Change Pagination Type to Search Engine.
    3. Sometimes you may which to turn Heading Type from Custom to Off, or at least disable the sort (by adding order by to SQL, or disabling sort on columns)
  3. Modify Item properties
    1. Change Type to Radio Group
    2. Change Number of Columns to 4 (something relevant to your list. Usually useful for items with small number of options)
    3. Change template option Item Group Display to Display as Pill Button
  4. Modify Page Property
    1. Set Execute when Page Loads
      $('#P29_DEPT_Z_CONTAINER').appendTo('#zhuzh .t-Region-headerItems--buttons')
      This moves everything holding the radio group together, to a spot made for buttons in the region.
      Not something I do often, but can be an economic use of space.

Notes


We've found the search engine pagination style great for touch devices, but I tend to prefer the Display Position on the Left, and at the Top, or at least Top & Bottom.

Report Template Options offer a facility to hide pagination when all rows displayed, but I've never seen it consistently work as I expect, so I continue to use my own JS library call for that.

The static ID can be whatever you like, so long as it doesn't clash with other IDs on the page, such as item names.

See my next post for more detail on how I derived that line of JavaScript, and what you can do with the browser tools.

See a video on how to action this blog here.

Tuesday, 18 February 2020

APEX Component Settings for Switch

While I point out a 'hidden' setting for Switch items in APEX, I want to comment on two related item types found in Oracle APEX => radio buttons and checkboxes.


1) Radio button

I really think the standard Template Option for radio groups should 'Display as Pill Button'

It really provides a simple and effective UX when a small set of options are on offer. Touch devices can select the option as if it's a button, and mouse users only need one click, compared to a select list requiring two. And the click doesn't need to be precise for those tiny radio group circles. I wish the application builder adopted the pill button approach more often.

And it's real easy to hook a dynamic action on change of the radio group, perhaps to refresh a classic report with data limited to the selected option. (Don't forget to add page items to submit!)

2) Switch

The Switch item was the answer to a number of methods of deploying an "iPhone-like" on/off switch.
I actually wrote an (internal) item plugin to do just this, but I much prefer using the native option.

A frequent question relates to how switches are rendered. If you don't see what you expect, have a look in Shared Components -> Component Settings.
Application -> Shared Components -> Component Settings

Personally I prefer the 'Pill Button' look, for reasons above. I find the APEX Application Builder seems more washed out with all the Switches in the properties bar, compared to the more softened On/Off pill buttons. If I could change this at the builder level, I would.

The offerings will vary depending on your APEX version, and if the application's Universal Theme has been refreshed

Application level settings for plugins can also be found in Component Settings, in addition to built-in APEX feature tweaks. These can something you choose to review after an APEX version upgrade.

3) Checkbox

It seems the humble HTML checkbox will always be extant, regardless of the pain it brings to tabuler forms. However, it suffers the same precision requirement as the native radio group selections - though better deployments allow selection using the label as well.

Go with the Switch (as a pill).

What do you think?


Wednesday, 29 January 2020

Unpivoting Oracle APEX meta-data

There are APEX dictionary views for most of the data that represents the 'source' that is your APEX application meta-data.

Note: This post has beeen updated to reflect me not looking very hard, but I added a performance comparison... just because.

Today I found one place where I really wanted to run a query to find references to potential data - application substitution strings.

APEX Application Substitution Strings

And yes, I have a CSS rule to right-align those names, to make it easier to read.

I couldn't find any references in the APEX dictionary (correction, see below), so I looked in the Oracle data dictionary to find where it may live.
select * from all_tab_columns 
where column_name like 'SUB%03';
So not only are these stored in a place inaccessible to us mere-mortal developers, they are also stored in 20 sets of name/value columns - not rows.

This means if you have a dozen applications, with references in slightly different locations for each application, then string searches might be a pain.

Unpivot to the rescue!
select * from (
  select id app_id, alias, name -- key facts
      -- all the substitution stringz!
      ,substitution_string_01, substitution_value_01
      ,substitution_string_02, substitution_value_02
      ,substitution_string_03, substitution_value_03
      ,substitution_string_04, substitution_value_04
      ,substitution_string_05, substitution_value_05
      ,substitution_string_06, substitution_value_06
      ,substitution_string_07, substitution_value_07
      ,substitution_string_08, substitution_value_08
      ,substitution_string_09, substitution_value_09
      ,substitution_string_10, substitution_value_10
      ,substitution_string_11, substitution_value_11
      ,substitution_string_12, substitution_value_12
      ,substitution_string_13, substitution_value_13
      ,substitution_string_14, substitution_value_14
      ,substitution_string_15, substitution_value_15
      ,substitution_string_16, substitution_value_16
      ,substitution_string_17, substitution_value_17
      ,substitution_string_18, substitution_value_18
      ,substitution_string_19, substitution_value_19
      ,substitution_string_20, substitution_value_20
  from apex_190200.WWV_FLOWS -- direct from underlying view
)
unpivot (
(str, val) -- new columns
for rec in -- denoted by 
  ((substitution_string_01, substitution_value_01) as '01'
  ,(substitution_string_02, substitution_value_02) as '02'
  ,(substitution_string_03, substitution_value_03) as '03'
  ,(substitution_string_04, substitution_value_04) as '04'
  ,(substitution_string_05, substitution_value_05) as '05'
  ,(substitution_string_06, substitution_value_06) as '06'
  ,(substitution_string_07, substitution_value_07) as '07'
  ,(substitution_string_08, substitution_value_08) as '08'
  ,(substitution_string_09, substitution_value_09) as '09'
  ,(substitution_string_10, substitution_value_10) as '10'
  ,(substitution_string_11, substitution_value_11) as '11'
  ,(substitution_string_12, substitution_value_12) as '12'
  ,(substitution_string_13, substitution_value_13) as '13'
  ,(substitution_string_14, substitution_value_14) as '14'
  ,(substitution_string_15, substitution_value_15) as '15'
  ,(substitution_string_16, substitution_value_16) as '16'
  ,(substitution_string_17, substitution_value_17) as '17'
  ,(substitution_string_18, substitution_value_18) as '18'
  ,(substitution_string_19, substitution_value_19) as '19'
  ,(substitution_string_20, substitution_value_20) as '20'
  )
)
order by app_id, str 
This query transposes all the string/value columns into rows, each denonimated by the new "REC" column.

Note, this query can only be executed by those with the APEX Administrator Role, and if you want to have it within a view that could be executed by other schemas, then select access on wwv_flows is needed with grant option.

Columns unpivoted into rows

By placing the query in a view, I can now make queries like this to find any substitution strings in any applications that have date references.
select * from apx_app_sub_strings
where val like 'APP_DATE%';
Pretty neat, well at least as far as what I was trying to do.

This query may break in future versions, as it's based on an underlying, undocumented view.
I also think that once upon a time, there were fewer pairs.

Update: As the community quickly pointed out, I missed & forgot about a view already dedicated for such a task. I was too busy looking for a column number, when really I should have used APEX_APPLICATION_SUBSTITUTIONS.

I thought I'd see how 'they' solved the problem, and I was a little surprised to see a bunch of UNION statements.
SELECT ...
           f.substitution_string_18,
           f.substitution_value_18,
           f.substitution_string_19,
           f.substitution_value_19,
           f.substitution_string_20,
           f.substitution_value_20
      from wwv_flow_authorized auth,
           wwv_flows f
     where f.id = auth.application_id )
select workspace,
       workspace_display_name,
       application_id,
       application_name,
       substitution_string_01 as substitution_string,
       substitution_value_01  as substitution_value
  from substitution
where substitution_string_01 is not null
union all
select workspace,
       workspace_display_name,
       application_id,
       application_name,
       substitution_string_02 as substitution_string,
       substitution_value_02  as substitution_value
  from substitution
where substitution_string_02 is not null
union all...
After seeing this I couldn't help but run a brute for comparison, running both solutions x times.
iterations:5000
16.55 secs (.003310 secs per iteration) -- union all
 6.54 secs (.001308 secs per iteration) -- unpivot
I made sure the underlying join was the same, and I'm not all that surprised the unpivot did the job quicker.

Update 2: It appears 20.1 has gone with unpivot.

Thursday, 16 January 2020

Performance on Classic Reports with LOVs

Just an observation made yesterday that I thougt was worth everone's consideration.

I created a classic report on a reference table with a touch over 1000 rows, with pagination set at 150 rows per page.

That’s all I had on the page.

The table had it's own set of lookups for a couple of columns, so I assigned two LOVs I already defined in Shared Components.

Classic Report LOV

Next minute, I had a simple reference page taking 10 seconds to load.


This is information from apex_workspace_activity_logs, showing results where no LOV was applied, two LOVs, and just one. I later found the number of rows shown in the pagination set also varied the result.

As I removed one of the LOVs, I quickly realised this was the problem.

I ran the page in debug mode, to see if something crazy was happening as it constructed the query.
When I used debug = YES, it pinned all the effort onto the one line item - but not the query itself.
rows loop: 150 row(s)

Looking again using LEVEL9, I could see that every row took a bit of work, not just the query.
In fact each row had three debug line items referencing my LOV lookup SQL.
begin begin SELECT name display_value, id return_value  
bulk collect into wwv_flow_utilities.g_display,wwv_flow_utilities.g_value 
FROM  my_secondary_ref 
WHERE SYSDATE BETWEEN eff_start AND COALESCE(eff_end, SYSDATE)
ORDER by 1
; end;
end;
Debug chart

Apparently these are only melded into the SQL for IR/IG, not Classic Reports. Context switching kills the page instead - well, at least 149 extra executions of the one statement.

I ran another test so I could check v$sqlarea, and sure enough, there are far more executions of this lookup tha necessary - unnecessarily churning the CPU.

v$sql_area status

I’ve tended to embed these in my queries anyway, often as some form of scalar subqery.
This is a habit I started as a Oracle Forms developer, since Post-Query lookups made repeated network calls that just slowed the application down.
select sm.*
  ,(select name
    from my_lookup m
    where sm.id = m.id) my_desc
from some_model sm
This makes me really wonder how much of our existing page generation time is spent on this work?
Even on small classic report regions. This all adds up.
If only there was a way I could find all occurences of LOVs used in classic reports... wait a minute! I can query the APEX dictionary! ;p
select application_id, pagE_id, page_name, region_name
  ,(select maximum_rows_to_query 
    from apex_application_page_regions r
    where r.region_id = c.region_id) nbr_rows
  , column_alias, heading 
  , named_list_of_values, inline_list_of_values
  , column_is_hidden, condition_type
  ,build_option, column_comment
from apex_application_page_rpt_cols c
where application_id = 102
and display_as_code = 'TEXT_FROM_LOV'
order by nbr_rows desc
It's a shame such a nify declarative feature impacts performance this way.

Friday, 13 December 2019

Friday Funny - Festival of serious yahoos

I don't care what you all think, this is my blog, and I continue to laugh at this SQL joke.

select column_value
from apex_string.split('YAHOO~SERIOUS~FESTIVAL', '~')
order by dbms_random.value

If you're a Simpsons fan, you may recall this clip

Yahoo Serious Festival - The Simpsons
If you're not aware, Yahoo Serious is an Australian actor from the movie of the same name. He made it onto Time magazine, then nothing.

But how nifty is that apex_string.split function? And it's cousin join?
Although recently I saw Connor use what appears to be a native option, one that doesn't require an APEX package. On the blog topic list...

Tuesday, 10 December 2019

Where would we be if we just believe?

As a science aficionado, there are certain phrases that ... catch the eye.

Recently on twitter there was an interesting thread that continued from Michelle Skamene's post on Top 15 Tuning Tips for APEX.  Michelle provided a wonderful follow-up post summarising the outcomes of the thread.

Point 9 suggests we avoid HTML in our queries, and use HTML expressions. This is undeniably good practice, but there was a question regarding how much performance is gained. Patrick Wolf summarises it well (sorting, XSS, context switching).

I've been curious about this for a while myself, and what better way to verify the truth than do some science ;p

I thought about the recent experimentation I did with interpreted code, and recent client work I've done with bind variables, so I thought I'd not only compare timings with a simple test harness I use, but see what v$sqlarea had to say.

When considering what SQL to compare, I decided to use the simple bit of SQL I used for my AskTOM Office Hours demo app. I saw how quickly the embedded HTML expanded to fit repeated business rules, and why not use something simple. If any difference was to be seen, let's see if it shows up with something basic, just like Juergen suggested.

The first query is just a simple query on scott.emp, with an extra expression to determine if the row is 'special'. This information is used declaratively within APEX, so these calculations are absent from this testing. The complexity has been transferred, but simplified. That's the reason it's best practice.
cursor c_1 is
select /*+ qb_name(c1) */ empno, ename, job, hiredate
  ,case
   when hiredate < date '2000-01-01'
     then 'special'
   end last_cent
from scott.emp
So, let's just concentrate on the differences in the SQL we can measure.

The second query has the HTML tags embedded, repeated on columns where I substituted the column into the class attribute in the previous example. The code required expands quickly, hence the argument to keep the SQL and presentation layer separate.
select /*+ qb_name(c2) */empno
  ,'<b>'||ename||'<b>' ename
  ,case when hiredate < date '2000-01-01'
    then '<span style="color:purple;font-weight:bold;">'
     ||'<span title="'||job||'">'||to_char(hiredate,'fmddth Mon YYYY')||'</span>'
   end ||'</span>'
   as hireDate
  ,case when hiredate < date '2000-01-01'
     then '<span style="color:purple;font-weight:bold;">'||job||'</span>'
   end job
from scott.emp
The third query introduces context switching to PL/SQL due to the inclusion of htf.bold and apex_escape.html. This protects the output we'd otherwise have escaped using the relevant property. Or you could mitigate usage by sanitising data on the way in.
cursor c_3 is
select /*+ qb_name(c3) */empno
  ,htf.bold(apex_escape.html(ename)) ename
  ,case when hiredate < date '2000-01-01'
    then '<span style="color:purple;font-weight:bold;">'
     ||'<span title="'||apex_escape.html(job)||'">'||to_char(hiredate,'fmddth Mon YYYY')||'</span>'
   end ||'</span>'
   as hireDate
  ,case when hiredate < date '2000-01-01'
     then '<span style="color:purple;font-weight:bold;">'||apex_escape.html(job)||'</span>'
   end job
from scott.emp;
If we just compare throughput, there is a clear loser. Context switching between SQL & PL/SQL is just too much, though I would like to think in recent versions of the database, those functions could be enhanced with such devices as the UDF pragma?
iterations:50000
     3.98 secs (.0000796 secs per iteration)
     5.16 secs (.0001032 secs per iteration)
    33.09 secs (.0006618 secs per iteration)
I just run these queries lots of times to measure throughput. Tom Kyte wrote a more enhanced test bed called runstats, if you want juicier details.
-- and start timing...
    l_start := dbms_utility.get_time;

    FOR i IN 1 ..c_iterations
    LOOP
     for x in c_1
     loop
        null;
     end loop;

    END LOOP;
    l_end := dbms_utility.get_time-l_start;
    dbms_output.put_line( TO_CHAR(l_end/100, '99990.00') 
                       || ' secs ('||(l_end/c_iterations/100) || ' secs per iteration)' );
What about v$sqlarea?
select substr(sql_text, 1, 22)sql, fetches, parse_calls, buffer_gets, sharable_mem
   ,persistent_mem, runtime_mem, user_io_wait_time
   ,plsql_exec_time, cpu_time, elapsed_time, physical_read_bytes 
from v$sqlarea  
where sql_text like 'SELECT%SCOTT.EMP%'
order by 1

v$sqlarea results

These statistics validate the timings, and I think also validates this as a performance tip that belongs in Michelle's list.
While performance difference with/without HTML may be marginal in this case, the supplementary benefits make it clear best practice.

Tuesday, 3 December 2019

Include new APEX templates in an older APEX instance

Have you seen that super awesome theme in the new APEX version, then wondered how long it will it be before your site upgrades so you can actually use it?

What if I told you that your current version could be retrofitted to use that template?

I really like the look of the Content Row template, I think that's going to serve many developers good purpose.
Sure, I could create something similar now, but if I do as much as I can the same, then it should grease the wheels come upgrade time.

Content Row 18.2 vs 19.2

To make this happen, I
  1. Downloaded APEX 19.2
  2. Copied the /theme_42/1.4 folder to my 18.2 instance
  3. Created the report template, including template options
  4. Added the region component in my app
The first two steps are all about making the relevant CSS available to your instance.

After I had the /1.4 folder in place, I thought I would need to increment the Theme folder, but that just introduced other issues.


Instead, I just referenced the ContentRow.css file in the pages I wanted to use it.
This is the path where you can find it, so it can be placed wherever you like on your older instance.
/i/themes/theme_42/1.4/css/core/ContentRow.css
Alternatively, you could include this for all pages in the User Interface attributes.

Then I needed to create the named column (row template) in my 18.2 application, details of which I just copied from a 19.2 instance.

Content Row syntax

I can even transfer the template options, so I started transferring from details on the page
You can also use this query to find instances within my 19.2 application.
select name, display_name, display_sequence, css_classes, group_id
  ,(select display_name
    from APEX_APPL_TEMPLATE_OPT_GROUPS
    where template_opt_group_id = group_id) group_name
  , help_text
from APEX_APPL_TEMPLATE_OPTIONS 
where application_id = 32532
and report_template = 'Content Row'
order by display_sequence
Then I created the template options in 18.2.

Add Template Option

And sure enough, they become available on my 18.2 region component.

Page Designer outcomes

Due to the differences in availability of template option groups, some manifest a little differently, but I personally prefer a one click checkbox to the more fiddly select list.

All that said, I'm not sure I'm entirely sold on the small screen format, though maybe thing will change when I get more realistic data in there.

Content Row - mobile

Now I'm sure when I change over to use the actual template when I'm in 19.2, I'll need to re-apply the template options - but Shakeeb likes the idea of retaining shared template options, so you never know.

This was all done in 18.2. If you're on an earlier version, your mileage may vary.

If you're not using the Universal Theme, then you're probably not going to get very far.