Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

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!

Thursday, 5 September 2019

Paste from clipboard in Oracle APEX

Since APEX makes it so nifty, I've got a few pages simply used as query tools - handy to verify data during development.

I also use these pages to experiment with UX, and recently I thought I could save myself pressing ctrl+v to paste my ID into a field for lookup.

I knew we had done something recently for adding content to the clipboard, so I figured there would be a way to paste clipboard content into a field.


Sure enough, Dr Google had the answers, though it's bleeding edge. Good enough to experiment with.

For me, adapting this response to APEX meant setting the provided field name.

I added this to my JS global declaration.
async function paste(input) {
  const text = await navigator.clipboard.readText();

  $s(input, text);
}
And this when I clicked the button to action the paste, I nominate the item I want the clipboard data to be copied into.
paste('P105_EVENT_ID');

An on-change dynamic action on this field would add a member to the collection, and refresh a number of regions that query the collection.

Don't forget to add Page Items to Submit

I have an example here.
https://apex.oracle.com/pls/apex/f?p=32532:22

I thought I noticed a potential odd behaviour with this, possibly due to the async action.
Therefore I've created an example with a few regions, and I'll try throttling the connection to see if that highlights the behaviour.

And so now I have this as a handy reference, I'll demonstrate some APEX collections and regular expression behaviour while I'm at it.

Monday, 26 August 2019

Oracle APEX Radio Group null option fix

APEX 5.1 introduced a template option that transformed radio groups into pill buttons.


I love this concept, it makes for a pleasant UI - an easier target for mouse clicks and finger taps.
I liked it so much I tried to do this myself prior to 5.1.

In 18.2 (fixed in 19.1), there's an issue with the way the null option presents for these radio groups - it's not in the same row.


Marko mentions a CSS fix for this problem in his post, but I find it has some side effects.
.apex-item-grid.radio_group{
  display:flex;
}
Stretched items are no longer stretched, and it ignores the Number of Columns attribute.


I went to inspect the generated HTML, and compare it to the 19.1 working solution, expecting to find some difference in CSS, but I quickly realised the null options is just generated in the wrong level on the tree.

You can also test this manually by using the in the browser tool to drag/drop that element to its siblings in the grid-row.


This means the problem can be solved with JavaScript when the page loads.
$('.apex-item-grid > .apex-item-option').each(function(){
  $(this).prependTo($(this).next('.apex-item-grid-row'));
});
This JavaScript identifies any radio options that are immediately below the grid, instead of being within the grid-row. Then for each instance, it moves that element to the next sibling, which is the grid-row.


Using appendTo() will push it to the end of the list.

And this solution honours the stretch option.


Is there a better way?
Other than upgrade to 19.x ;p

References

jQuery Cheatsheet

Wednesday, 24 April 2019

APEX Classic Report as Alerts

I love classic reports.
You can make your data look like anything, out-of-the-box, as previously highlighted by Christina and Carsten.

When you create a classic report, you can select different Report templates via the region attributes. I use the Cards and Alerts a fair bit, allowing me to produce data driven content, instead of whatever I type in the application builder.

This has been the general premise of APEX templates since the beginning. We define a query, and feed those columns to the template, where the values are substituted in.

Alerts Report Template
We can combine this template with the following query, containing matching column aliases.
select 
  'Alert' as alert_title
 ,'Be alert, not alarmed.' as alert_desc
 ,null as alert_action
 ,'danger' alert_type 
 ,'fa-user-woman' alert_icon
from dual
This doesn't need to be from dual, it can be any query you like, as long as it contains those column aliases. Multiple rows will show multiple alerts, down the page. The alert_type can be any of the pre-defined template options for that attribute.

Over time these templates get upgraded, and now we operate with the Universal Theme, we gain access to improvements after verifying the theme.

For instance, APEX 5.1 introduced #CARD_COLOR# attribute to limit the manual CSS required to make it happen.

What the Alerts Report template still lacks is the ability to customise the icon (as at 19.1).


We can customise the icon with a declarative Alert Region, by setting the Template Option Alert Icons to 'Show Custom Icons', and specific the icon in the Icon field.

If we want to customise the output of a classic report displayed as an alert, we can use an After Refresh Dynamic Action that executes JavaScript on page load to rejig the HTML of the report.
// replace icon in template, since we can't do it in SQL yet
$('#acting .t-Alert').removeClass('t-Alert--defaultIcons').find('.t-Icon').addClass('fa fa-user-woman')

Here I find the region I gave a static ID of 'acting', then removes the default icons from the component with the t-Alert class.
It then finds the t-Icon class to extend with my chosen icon.
This transforms it to exactly what a declarative Alert region looks like.


This example was built for a report with only one record, but hopefully gives you an idea of the potential these type of events have. Check out similar example here.

Happy APEXing.

Saturday, 30 March 2019

Hide Pagination if one page results - Oracle APEX

I love this period of development not long after an APEX upgrade, where I discover all these little improvements that will help us out. Stuff that's not quite noteworthy in the wonderful read that is the new features guide (that's not sarcasm. Read it. Now.)

Recently I made an observation regarding a handy new template option, one that hides pagination if there is only one page of records. I've had a bit of jQuery doing this for a while, and I thought I had blogged about it already, but it was a similar feature regarding no data found.

Sure enough, it was still sitting in an email labelled 'blog post'. Now I could write this off, like quite a few others where a built-in feature got released before I got around to writing up a post. But I decided to post about this one anyway, in part because it may take some time for some to reach 18.2, and I think it's part of an interesting conversation regarding the evolution of the product.

Some of the improvements in each APEX release are likely the result of
- a tweet about a feature
- a forum post asking about a quirk
- a conversation at a conference, likely while at least one person was holding a beer
-  a request on apex.oracle.com/vote

If you notice something odd, or think something you've done would help everyone, put it out there.  Chuck it on the forum. Make it your first blog post. You'll help the community, and reap the benefits yourself when the time comes to upgrade to that version with some delicious fruit.

--***

So, that being said, here was the template option, available in APEX 18.2, but only to existing applications only after refreshing your theme.

Classic Report Region Template Option

Applied to a classic report, it will hide certain pagination styles with there is less records than that specified per page on that report.

This will save me creating a dynamic action on the region, calling a small bit of jQuery I have in a library. It's also something I figured could be practice for turning into a plug-in, but I never got there, either.

The trouble is, this template option doesn't appear to work with my favourite pagination type (another reason for the post).

I like to use the Search Engine pagination style, on the top left - available to the user regardless of vertical/horizontal width of the region. I have chapter in my book describing how to turn standard HTML links in this search engine pagination for a legacy theme into buttons.


Trouble is, it seems a little superfluous when there's only one page of results. And it takes up vertical space. Hence the idea of the template option.

With a little JavaScript invoked in a dynamic action after refresh on the region - this is the default dynamic action when you right-click to create dynamic action on a region in page designer. Kudos to the APEX developers responsible for those little touches.


Here's the code I used to do the job.
if ($(this.triggeringElement).find('.t-Report-paginationText').children().length == 1
  && $(this.triggeringElement).find('.t-Report-paginationText').children().text() == 1)
    $(this.triggeringElement).find('.t-Report-pagination').hide();
A few points:

  • this.triggeringElement refers to the region, since that's what the dynamic action is firing off
  • find() is looking for page elements with the t-Report-paginationText class, in this case, the button group
  • children() counts how many buttons, so true if there is only one
  • I added the second condition when I realised it was hiding results when there were 11 pages of results, so I check if the button contains the text "1"
  • then it applies .hide() to the pagination group

Note, this is designed for Search Engine pagination in the Universal Theme, and the template option probably works with a different mechanism. There might be a clever way to solve this with CSS?

The real solution also belongs in a library so it can be re-used. I did it like this once, but I'm sure it could be neater.
function hide_pagination_iff_one_page (p_region) {
  if ($('#'+p_region+' .t-Report-paginationText').children().length == 1
  && $('#'+p_region+' .t-Report-paginationText').children().text() == 1)
    $('#'+p_region+' .t-Report-pagination').hide();
}
I would then put this in the after refresh dynamic action. It passes the ID of the region triggering the action, ready for embedding in the selector.
hide_pagination_iff_one_page($(this.triggeringElement).attr('id'));

So my open question is, how would you tidy such JavaScript? how would you place it in a library?
I'm still getting the hang of namespaces, and how to organise our stubs.

Thursday, 1 November 2018

Lazy Loading Menu Count

I've been using the lazy loading concept demonstrated in Maxime's post quite a lot recently, I'd love to see this as a declarative feature one day.

I also wondered if I could apply this concept to the badge count in the side menu - not slow the page load by a longer running query that populates the count.


Turns out it wasn't that hard, particularly since I already had the jQuery I needed from a previous requirement.

We first need to add a unique class to the link definition, so we can identify the menu item that needs updating.


The label contains the substitution string reference to an application item called LAZY_MENU, that is just computed to zero on each page load.
Surrounded by square brackets, this turns it into the count badge.

Add a dynamic action that executes PL/SQL on Page Load, probably to the global page, though this demo only fires when on page 15.


The demo populates the item based on a query that counts how many columns in my schema, plus a random number, so we can see it change each time.

A subsequent JavaScript action is where the real action is at
$('#t_TreeNav span.lazy-menu')
  .closest('div.a-TreeView-content')
  .find('span.a-TreeView-badge')
  .text($v('P15_COUNT'));
The selector identifies the menu item based on the lazy-menu class entered in the link definition, then traverses the tree to locate the relevant badge, which is then updated to the page item.

A more complete solution may also update LAZY_MENU application item, ready to have reasonably fresh information for the next page load.

Or we could make our queries faster ;p

Wednesday, 5 September 2018

Client Side Dynamic Actions using jQuery Selectors

Consider a data entry page where it might be nice to capitalise the first letter of a person's name, for a number of fields.


I understand I may be anglicising a problem that contains minutia, but focus instead on the thought processes and options we have available using Dynamic Actions.

Let's say we want to create a dynamic action that responds to change on any of those name fields, then runs some JavaScript to apply sentence-case to the name value, before the user submits the page.

We can do this declaratively just using the mouse, APEX will construct a comma delimited list for you as you select page items from the list.

Declarative item selection

But that's not the only way we can nominate components on a web page. The example above might create a selector for those items that is a comma delimited list of IDs.

$('P18_FIRST_NAME,#P18_MIDDLE_NAME,#P18_LAST_NAME');

When we have a look at the underlying HTML defined for these items using Inspect Element, the selector would locate these fields based on the id="P18_FIRST_NAME"

Inspect Element to see underlying details of page components


Alternatively, we could use classes. This is how web developers of any ilk build their web pages. They make up a string that means something to them, then associate some behaviour with it.
You don't need to "define" a class anywhere, but it pays to ensure it's unique, and follows a standard.

In our case, if we could add a class to each item, then we would only need to list one class in the dynamic action.
In some circumstances, this style of coupling behaviour could be more advantageous.

To make "initcap" appear as a class, as shown in the item as highlighted above, add the string to the 'CSS Classes' in the Advanced section of the item properties.


APEX Page Builder makes this task quicker, thanks to behaviour inspired by Oracle Forms - well, some of us ex-Forms programmers will recognise it as such.

If you select multiple items, you can change some properties in bulk.


Over in the properties, we can modify CSS Classes attribute for all three items at once.

Multi-item select in Page Designer

Note the 'Value Placeholder' attribute - this is different for each field, so the delta symbol is shown with the attribute value blued-out. I love this concept.

So back on the dynamic action, we can change the 'Selection Type' to jQuery Selector, and reference the class with the relevant syntax - prefixed with a full-stop, as opposed to the hashtag for IDs.

.initcap

Dynamic action, only when item value all lower case

We also didn't want this behaviour to apply only when all the letters are still lower-case. If the user wants to enter "von Braun", I won't overwrite their particular usage.

So note the client-side condition, emphasis on the 'client'. Most conditions on APEX components are server-side, which generally means they are evaluated during page render - do we or do we not include this button/region/item? Dynamic actions have client side conditions to decide whether to apply the True actions, or the False actions.

$(this.triggeringElement).val().toLowerCase() == $(this.triggeringElement).val()

Here I've referred to the item being triggered using this.triggeringElement, mentioned in the inline help for this particular attribute. Wrapping that expression with $().val() gives me the item value, or I could have used apex.item().getValue. jQuery built in .toLowerCase() does what you would hopefully infer.

As with all things programming, there are many ways to cook an egg, and this goes for setting the value. Here I've define a Set Value action that uses a JavaScript Expression, as there is no need for a round trip to the database server.

True Action - apply change to item value

This expression uses a function I found on Stack Overflow that I was happy with

toProperCase($(this.triggeringElement).val())

I placed in an application level JavaScript file, included via User Interface attributes of the application.
// Turn mcdonald into McDonald
// only run/apply if current string lowercase
// $(this.triggeringElement).val().toLowerCase() == $(this.triggeringElement).val()
function toProperCase(s)
{
  return s.toLowerCase().replace( /\b((m)(a?c))?(\w)/g,
          function($1, $2, $3, $4, $5) { if($2){return $3.toUpperCase()+$4+$5.toUpperCase();} return $1.toUpperCase(); });
}
Most of the JavaScript usage I have in my applications these days tend to be one line, or is some form of expression, so commonly used there is only a small handful.

Don't let a little JavaScript scare you away from enabling useful interactivity for the end user. Dynamic actions do most of the work for you :p

Tuesday, 15 May 2018

Transposing data using UNPIVOT

A couple of years ago I posted a method to remove nulls from a report using the Value Attribute Paris - Column template.

Here's an example of how we might utilise the region, within the breadcrumb region position.

Note - some values may have been adjusted from this screenshot for their protection.

Any nulls were shown as a tilde, then hunted down and eliminated with some jQuery that executes after refresh of the region, and/or on load of the page

Cool idea, can be improved.

And when the JavaScript was placed on one line, it seems so innocuous. It works, so what? What's the harm?
$('dd.t-AVPList-value').each(function(){if ($(this).text().indexOf('~')>0) $(this).hide().prev().hide()})

The trouble with my original method is that it's executing jQuery after the page is rendered. This represents extra work that could be eliminated. The same justification was present in Oracle Forms, as Post-Query triggers were not preferred.

And of course the applies to DML being applied to the database. Sure, you may need some conditional processing and apply a zillion single updates, or you could write some elegant SQL to do it within one update.

The other problem is that the Universal Theme responds to this change with some content movement that can frustrate the user - the body bubbles up to meet the removed content.

New and Improved Solution

Side-note: how can it be new and improved?

In this case I've been talking about columns from a relative simple query, as simple as selecting from scott.emp.

If we can transpose these results, like a magic wand you can do in Excel, then we could just use the Value Attribute Pairs - Row version of the template, and not worry about any jQuery that manipulates the page after it's rendered.

We tranpose columns by surrounding the existing query with a simple UNPIVOT.

select * from (
    select to_char(empno) empno
     ,ename
     ,job
     ,to_char(sal) sal
     ,to_char(comm) comm
    from scott.emp
    where empno = 7788
    --where empno = 7654
) 
unpivot 
(val
 for name in (
    (empno) as 'Emp No'
   ,(ename) as 'Name'
   ,(job) as 'Job'
   ,(sal) as 'Sal'
   ,(comm) as 'Commission'
 )
);

NAME       VAL                                     
---------- --------
Emp No     7788                                    
Name       SCOTT                                   
Job        ANALYST                                
Sal        3000   

4 rows selected.

... query executed with other empno

NAME       VAL                                     
---------- ---------
Emp No     7654                                    
Name       MARTIN                                  
Job        SALESMAN                                
Sal        1250                                    
Commission 1400

5 rows selected.

Notice the result using an emp with a commission shows more rows. The page will only render the data supplied so there no dynamic action required.

Default ordering seems to honour the order of elements in the FOR expression.
Datatypes of columns must match up, hence the to_char around the numeric columns.

Update - I saw Mike on the forums suggest that "transpose" implies a matrix, and offered a combined unpivot/pivot.

tl;dr steps

  • surround the existing query with unpivot
  • add any datatype conversions necessary to make columns the same
  • change report region from pairs with column to pairs with row
  • remove declarative column ordering
  • remove the dynamic action that finds the tildes

If you want to modify how something presents itself on an APEX page, there are other options to explore before jQuery
  • Template options
  • Conditional SQL, manifesting as HTML expression in a column
  • CSS solutions trump JavaScript.
Simplify, man.

Tuesday, 9 January 2018

Modify OracleJET Property at Runtime in APEX

OracleJET has attributes galore, but some are are (not yet) available to change at design time, so JavaScript code can be added to the chart attributes to set relevant attributes.
function(options) {
options.styleDefaults.threeDEffect = "on";

return options;
}
See my previous post about modifying these attributes on render.
We can also do this at runtime, perhaps as response to a button click, such as the 2D/3D button in the cookbook.

First, set a static ID on the chart, possibly one of the most common "advanced" properties I use.

Static ID property available on many components

Use this ID in the browser console at runtime to see what JSON was generated for the chart definition.
Any missing properties will use the default specified in the documentation.
apex.widget.jet.getChartJSON('p95_skew')

Browser Console results

The documentation on these attributes is thorough, but I'd love some examples to help keep me moving.
http://www.oracle.com/webfolder/technetwork/jet/jsdocs/oj.ojChart.html#styleDefaults.threeDEffect

We can modify the orientation property by supplying a name/value pair as a JSON string.
$("#p95_skew_jet").ojChart({'orientation': 'horizontal'});
Note the selector has is the static ID with a suffix: $("the_static_id"+"_jet")

It took a little while to find the correct punctuation for the nested properties, so this is really one of those blog posts I created so I don't forget. You've seen Alex's blog by-line, right?
$("#p95_skew_jet").ojChart({'styleDefaults':{'threeDEffect': 'off'}});
Not all runtime tweaks behaved as expected, however. The following property behaves as expected when setting on render, but at runtime it squishes the width of the entire chart.
$("#jet1").ojChart({'styleDefaults':{'barGapRatio': 0.2}});

Refreshing the chart region afterwards did not help in this case.
While looking for answers, I came across this post from Riaz describing similar customisations.

Wednesday, 14 June 2017

Presentation: Dynamic Actions, Javascript, & CSS for APEX Beginners

My first webinar didn't do so well, perhaps my new home will have suitable internet capability, in this "innovate nation" of ours...

My second one on my Boggex game went ok, though the room the locals were in was like an sauna.

My third attempt had it's own set of issues.
1) Ubuntu as an OS doesn't seem to enable broadcasting via GoTo Meeting.
2) The wireless policy in the Oracle building is effective, but not great for hastily setting up a second laptop
3) The navigation keys on Lino's keyboard was just different enough to be disruptive
4) While I use on Chrome's save password feature, I'm pretty confident with most of my APEX passwords. It's the username I was stuffing up on the foreign PC.
5) The think tank at HP thought it would be a good idea to map Aeroplane Mode to F12, so when I proceeded to display the Chrome browser console...

So balance was restored after the first 10 or 15 minutes, which rattled my first attempt at presenting using just an APEX app - no powerpoint/prezi.

Though I think the concept was fine. It's just like training people, jumping between Page Designer and runtime.

I'd give it another go, and it's less presentation prep. I learned a bit from the first session, and explored some layout options while I was at it.

I still baulk a little at the difference between talking with a live crowd, and a community of ears at the other end of my microphone. I wonder if (finally) creating a few more videos will fix that? Once my ultrabook is back from computer hospital, perhaps. In the new house?

As for the topic, I explored dynamic actions, JavaScript and CSS for those trying to understand the fundamentals. It's a favourite topic of mine and I think there are a few basic patterns that get me a long way in creating interactive applications.

No doubt I'll refine these example further, and how to communicate them more concisely. Stay tuned.

I've made the app available here. Please feel free to provide any feedback, either here or through the app.
https://apex.oracle.com/pls/apex/f?p=27882
My favourite page is P50 specifically on Dynamic Actions, probably the most versatile utility within APEX.
If you scroll down you'll see the relevant code I used to sustain an interactive page that doesn't need to submit.
Also look out for things like conditional buttons, conditional colours (foreground and background), APEX behaviour considerations, among others.

If you're in Australia and you'd like training on this topic, I could demonstrate cool stuff for hours ;p
I also aim to get some organisation around github sorted when I get my laptop back, but I've at least put the export here so you can check it out directly.
https://github.com/swesley/apex-beginner-css-jquery

I hope you learn something new.

Scott

Wednesday, 14 September 2016

Extend column tooltip to table cell

Quite sometime ago while still on 3.x I described a simple way to add tooltips in a report using standard HTML in a HTML Expression.
<span title="#RECENT_NOTE#">#MY_COLUMN#</span>

However, it will only appear when hovering over the span content, not anywhere within the table cell.

To do so we can add a dynamic action to execute some JavaScript after refresh of the relevant region.
$('td[headers=my_column] span').each( // for every data cell in the column
  function() { 
     // copy the title attribute to the parent cell
$(this).parent().attr('title',  $(this).attr('title'));
  });
This JavaScript copies the title attribute from the inner span to the surrounding td tag, so you will see the tooltip when the cursor is anywhere within the cell. I find this relevant when there is a bit of spacing/padding in the report.

In this follow-up post I describe how you could display this tooltip on tap of the cell, useful when used within touch devices.

Tuesday, 7 June 2016

Synchronous Dynamic Actions in APEX 5.1

If you've ever used a PL/SQL dynamic action with the default 'wait for result', you would have seen the following warning if you have the browser console open.

Text for bots: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.

Consider this scenario of dynamic actions on change of P42_ITEM:

Synchronous vs Asynchronous server calls
First JavaScript takes value of P42_ITEM, concatenates a letter and places result in another field. Let's say I entered "X":
$s('P42_NEW2', $v('P42_ITEM')+'a');
The value in P42_NEW2 would be "Xa"

The PL/SQL code concatenates another letter, passing current value of P42_NEW in and out of session state via page items to submit/return:
:P42_NEW2 := :P42_NEW2||'b';
The value will now be "Xab"

The second JavaScript concatenates another letter:
$s('P42_NEW2', $v('P42_NEW2')+'c');

On page load the value of P42_NEW2 would be "ac", since both JavaScript actions default to 'Fire on page load'.

If 'Wait for Result' on the PL/SQL action is 'yes' (default), then the communication will be synchronous and the JavaScript will only execute once the PL/SQL is finished. The final value would be "Xabc"

If the PL/SQL action 'Wait for Result' is set to 'No', then the subsequent JavaScript will execute immediately, not, um, waiting for a result. The value of P42_NEW2 would briefly be "Xa", then just "c" until the PL/SQL returns with "Xab".

Asynchronous DA result

Think about that. This impacts how you structure dynamic actions, pose questions to the user, and steer application workflow. Regression testing will be required when updating compatibility of your existing applications to 5.1.

In a tweet by John Snyders, who has posted some amazingly detailed posts on the inner workings of APEX, he suggested we can soon forget there was any other way.


I understood this to infer that synchronous actions would disappear. That screenshot is from 5.1 EA1, so it seems they're hanging around for now. It could be considered a backwards compatability thing, though it's still the default option. I'd say many applications would not work as expected should this behaviour disappear.

Synchronous activity on a website is usually non-preferrable for good UX, I understand that's inciting the change in the nature of the inherent browser behaviour (ie: the warning above). As far as I understand it, the way the APEX team has mitigated this warning is by modifying the JavaScript call to the PL/SQL callback as follows:
apex.server.process
  ("CB_AJAX" // name of AJAX callback
  ,{x01       : $v('P42_ITEM')}
  ,{async: false // deprecated synchronous option
   ,success: function(pData) {
     // deprecated wait for result
     $s('P42_NEW2', pData + 'c');
   }
).done(function(pData) {
  // 5.1 wait for result
  // ...
});
Well, this is the jQuery method for deferred callbacks. In future this will be done natively as promises. I've previously detailed some sample code on these variations.

So the end result to the developer appears to be very little, though I'm sure there were plenty of tweaks in the engine to make this happen. Though it does protect applications from when browsers do decide to no longer support the async parameter.

Edit: The end result for the user, as per John's comment, is the browser will not be locked up whilst waiting for the result. This behaviour probably will result in some behaviour differences in some applications, time will tell. Be on the lookout for it.

Actions are no longer synchronous, but you can still have JavaScript execute only after the PL/SQL finished.

Wednesday, 25 May 2016

More on CSS selector performance in Oracle APEX

Last month I wrote a post about CSS performance, including some performance test results.

I recently encountered this brilliant post on Medium that describes some best practices for CSS.

While APEX does a lot of this for you, I think it's worth a read by all developers. Even applying basic naming conventions can make code easier to read and understand.

There was also a section on performance that described an issue that my previous example didn't explore, and that's 'tree walking'. Each web page could be likened to a giant tree, and your CPU will like you if you can minimise the amount of traversing necessary to verify the component being sought.

So for APEX developers, consider the selector reading right to left, and try keep it to two steps.

For instance, to identify a button within a classic report, you would define a static region ID for the report, and use the following selector
#p1_region a.t-Button

This would look for anchors with the standard Universal Theme button class, but only if it exists within the classic report region. Concise & effective.

You could use this selector to apply further CSS attributes to the buttons, perhaps via the Inline CSS page attribute.
#p1_region a.t-Button { font-weight : bold; }

Or you could use it as a jQuery selector for an on-click Dynamic Action, responding to the button press. If you add the following as link attributes to the column with the link button. It adds an attribute to the anchor with information from a column aliased as season in your SQL.
data-season="#SEASON#"
Note, to render a column link as a button, this attribute would also contain this class listing, to look like the nearby image.
class="t-Button t-Button--warning t-Button--simple t-Button--large t-Button--stretch"

You can refer to this information within a JavaScript action to set a hidden page item with the data from the season column.
$s('P1_SEASON', $(this.triggeringElement).data('season'));

A subsequent PL/SQL action could then submit this value to session state and execute something related on the database, without submitting the page on click of the report row button.

While that has side tracked from selector performance, it shows how a simple selector can be used within APEX to trigger events. A pattern I use regularly, done with minimum effort.

If you like this post, you may like my book /plug

Thursday, 28 April 2016

On CSS/jQuery Selector Performance

My post describing the use of a simple selector identifying page spinners was originally going to be about performance, then I learned something I found very interesting.

I likened what I learned to Tom Kyte's essay (Asktom->Resources->Presentations->FalseKnowledge.htm) on Correlation vs Causation. The essence was that things change over time, and we can't always trust authorities on the topic, and we must always test in our own environments. This aligns with skepticism in general, and here we are applying it to web development.

The following forum post described the subsequent code
https://community.oracle.com/thread/3908020
.u-Processing
{
display:none
}
I was going to suggest simply prefixing the provide class with the relevant HTML tag.
span.u-Processing
{
  display:none;
}
But I discovered a thing or two while drafting the post to explain my reasoning.

A while ago I incorporated much of my jQuery performance knowledge from this article
http://www.artzstudio.com/2009/04/jquery-performance-rules/
It was concise and it made sense. It also harmonised with other information I found on the topic. However, the post is now 7 years old and is probably worth re-testing it's assertions.

Consider the provided solution using .u-Processing. The period prefix means it's looking for a component on the page with the class u-Processing, as per in the definition of the spinner.
<span class="u-Processing" ...

If the component had an id, you could use the # prefix to reference the ID, much like using an unique* index. (*in the web world, the an ID is not guaranteed unique, but should be in best practice, particularly to avoid logic errors.)

As I had previously interpreted, the problem was that it needs to look at every single component on the page. If the selector was prefixed with the span, this reduces the workload on the browser by filtering the DOM tree to just the span tags.
span.u-Processing
And of those span tags, which have the provided class.

The reasoning seems sound, right? Part of it relates to native JavaScript methods available for locating content. Now if only there was a way to test these assertions...

Well on jsperf.com you can set up such test cases and run them against different browsers. Similar websites are available for SQL (livesql.oracle.com) and JavaScript test cases (jsfiddle.net).

[Note: jsperf.com was alive when I started drafting this post in March 2016, but has been down for a few weeks in April? ]

In regard to browser performance, someone had already prepared an example based on the performance rules described.
https://jsperf.com/jquery-class-vs-tag-qualfied-class-selector/47
And the results on my laptop in 2016 painted a somewhat different picture.

Chrome / Linux / Laptop

And true to Tom's essay, when I tried the same tests across different browsers on a different platform, the results are widespread.


But like anything it really depends on the page you're running it on. I did find the test case a little contrived since it was based on a very small portion of HTML, so I attempted the same type of test using HTML extracted from a typical page generated from APEX to see how many entries it's actually sifting through.

http://jsperf.com/apex-tag-vs-id-vs-class


To give you a sense of scale, I ran some jQuery statements in the browser console of an even bigger page, one including a region selector so plenty of regions & reports.

This returns the number of elements on the page you could identity with a jQuery selector. In my case it returned about 3000.
$('*').length

There are only 148 spans.
$('span').length

And only one spinner.
$('.u-Processing-spinner')

Considering the amount of operations/second the browsers are capable of, these numbers may seem small, but everything adds up. It's worth keeping up with better practices to trim performance where ever possible.

So the lesson I read out of this is if your developing APEX applications and you have basic jQuery mechanics, or even just use jQuery selectors in your dynamic actions, then perhaps stick with a readable, logical option that you're comfortable with.

Either that or I still have a lot to learn about jQuery performance ;p

If anyone has good, current resource recommendations on the topic, I'd be happy to hear. Particularly if it relates to use in the #orclapex environment.

Sunday, 20 December 2015

Calling PL/SQL from JavaScript in APEX

When I first wrote Chapter 9 of my book, Choosing Processing Options, I knew I still had a lot of research and experimentation to do, particularly regarding the async parameter. Below is a summary of the outcome of this work and drove a full revision of my original chapter.

Last week I received a printed copy of my book and noticed that unfortunately the first submission of chapter 9 made it into print. Apress are looking into this for me, but please consider the concepts in this post refactored into what you read. It all relates to the following message you may have noticed in your browser console.


The following post is slightly modified from an email conversation considering workflow from a browser based system. There was a more detailed discussion in a forum post that I'd need to dig up.

Imagine a button on a screen to add a record that may already exist. How do you interact with the user?

--***
On 8 October 2015 at 09:05, Scott Wesley wrote:

Hi gang,

I've played with this, thought about it, and I think we're already on a good wicket with another pattern.

Option A)

On click btn ->
PL/SQL: try insert, set result to page item (eg: P0_SIGNAL)
Dynamic action on P0_SIGNAL ->
Notification: Display message based on item.

We need to shake it up a little to provide different colour message - success/failure etc.

Tom Petrus suggested a more elegant alternative (with one less step and/or didn't involve a page item?), but it was in transit time at kscope and it was a hard topic to explore without code. In revising this post, I think perhaps he set the message to some JavaScript container, which in turn is sent to the database using APP_AJAX_X01.

Option B)

On click -> test javascript condition, which actually calls a synchronous function to test existence in database
True action - PL/SQL to insert
False action - alert user

This is what we were attempting this afternoon, and sounded good in my head but there are a few things wrong.

  1. We're calling the db twice, when we just need to do it once. We're in an optimistic world now: try it, work it out once it's done.
  2. The web isn't built for it, and the specifications are making it harder for us to do so. A common sentiment on from MDN.

Below are the examples we attempted for the JavaScript to return true or false, hence triggering the appropriate true/false dynamic actions. Function b() represents better practice. CB_AJAX is an AJAX callback (PL/SQL) defined on the page and returns a number as a text string.

This works, is completely synchronous, but will only work in browsers for a finite time thanks to the highlighted row (async:false).
function a() {
console.log('a');
apex.server.process
  ("CB_AJAX"
  ,{pageItems : '#P9_EMPNO'}
  ,{dataType:"text"
   ,async:false
   ,success:function(pData) {
       console.log('Result a:'+pData);
       console.log('success:'+ (result.length));
       result = pData;
     }
   }
);
return result.length == 2;
}
The next example uses 'deferred objects', which is jQuery's updated method for handling "success", but we can't access the callback values outside that scope, so we can't return true or false. The 'callback' is the done() section.

"javascript return result from callback" is an obscenely googled phrase, with a fair response of: "This is impossible as you cannot use an asynchronous call inside a synchronous method."
If that hurts your head a little, welcome to JavaScript mechanics.
function b() {
console.log('b');
apex.server.process
  ("CB_AJAX" // this is just => htp.p('X');
  ,{pageItems : '#P9_EMPNO'}
  ,{dataType:"text"}
).done(function(pData) {
   console.log('Result b:'+pData);
   result = pData;
   console.log('success:'+ (result.length));
   return result.length == 2;
});
// anything here will run before plsql finished
}
Eddie suggested using "promises", which sounded promising. ha.
But they are just the native implementation of deferred objects. And extremely new, unsupported by even IE11.
I also think this quote is telling: "This is extremely useful for async success/failure, because you're less interested in the exact time something became available, and more interested in reacting to the outcome."
function c() {
  console.log('c');
  return new Promise(function(resolve, reject) {
    var jqxhr = apex.server.process
 ("CB_AJAX" // this is just => htp.p('X');
 ,{pageItems : '#P9_EMPNO'}
 ,{dataType:"text"}
).done(resolve);
  });
}

c().then(function(result) {
  // code depending on result
  console.log('.then');
  console.log(result);
  return result.length == 2;
}).catch(function() {
  // an error occurred
  console.log('error');
});
Then I tried letting functions accept callbacks, which really defines all logical sense I learnt in programming 101. If this doesn't screw your brain, google "doSynchronousLoop.js". I'm sure it's 21st century spaghetti code, with the added dimension of time.
function myCallback(pData) {
   console.log('Result b:'+pData);
   result = pData;
   console.log('success:'+ (result.length));
   return result.length == 2;
}
function d(callback) { // d
console.log('d');
apex.server.process
  ("CB_AJAX" // this is just => htp.p('X');
  ,{pageItems : '#P9_EMPNO'}
  ,{dataType:"text"}
).done(callback);
// anything here will run before plsql finished
}
d(myCallback);
So I think the conclusion to be drawn out of all this is that Tom Kyte is right, and always has been. Run code, if it raises an exception, let out propagate, politely let the user know; otherwise success and move on.

Anything else and you are going against the grain.

APEX provides the ability to do this declaratively with dynamic actions, stick with option A, and we'll improve the mechanism in future to incorporate a library to set/read P0_SIGNAL with a JSON string that will get converted into signal type/message and invoke Alertify as appropriate, as opposed to setting it with a single value as currently done in <redacted>.

Edit: If you've ready this post, you should also about async wait by by Vincent.

References


Thursday, 31 July 2014

Case sensitivities and making JavaScript more like PL/SQL

Yesterday the Anti-Kyte (a.k.a. Mike) published a great post on coding styles. I started to post a comment but ended up rambling to I thought I'd put my thoughts here.

I can't be one to comment on tangential intros (just look at any of my presentations), but he almost lost me when I thought the entire post was going to be soccer jokes I didn't get but switched in time to three controversial topics:
  1. Uppercase keywords
  2. Camel case
  3. ANSI joins
To add my 5 cents - I still tend to uppercase keywords - partially out of habit and sometimes standards are a client site. I still like the concept of the brain finding lower case easier to interpret than upper case - now with extra help thanks to syntax highlighters. I don't mind all lower case, though I feel a little naughty when doing so. I know Connor does it all the time, and it looks raw & simple - which is not a bad thing.

He scared me a little when he started to provide PL/SQL examples using camel case - I can't stand camel case.
My identifier naming conventions in JavaScript tend to match up with my PL/SQL preferences, though sometimes I find it to be a hybrid in occasions where the name might harmonise with other jQuery functions.
function p42_show_info (t) {
  theRow = $(t).closest('tr').addClass('highlight');
  l_order = theRow.children('td[headers=ORDER_NO]').text();
...
I should probably stick with one way or the other since bugs thanks to tolerated case sensitivity are the worst kind of bugs - parasites, if you will.

I have no problems using ANSI syntax when used explicitly (no natural joins), but I tend to only use them for outer joins and when readability would benefit. There are scenarios where ANSI outer joins are the best solution to a query.

Scott

Thursday, 29 May 2014

Enhancing APEX "no data found" message

Truth be told, the default "no data found" message for APEX reports is pretty boring.

Out of the box

Even the default wording irritates me just a little bit every time I see one amidst a page I thought was looking pretty snazzy.


There are two things I've done recently to make it look a little better.

1) Surround it with a nice soft coloured box.

Something with a little pop

This is pretty simple, just add the following CSS to your page (Edit page properties -> Inline CSS) or application's .css file. Tweak it to your heart's content - just no blinking text, please.

span.nodatafound {
  font-size:120% !important;
  border: 1px solid #FC0;
  background: #FFC;
  color: #384F34;
  display: block;
  font-weight: bold;
  margin: 2px auto 14px;
  padding: 15px !important;
  text-align: left;
}

2) Customise the output

Speaking of cats (if you clicked through the reference), there are probably a few ways to skin this one.

If you create a dynamic action the fires after refresh of your report region, you can execute this JavaScript

var ndf;
switch($v('P4_REPORT_SEARCH').length)
{
case 1:
  ndf="One character and you found no records?!";break;
case 2:
  ndf="Make sure there is no type";break;
default:
  ndf="No employees by this name, sorry.";
}

$('span.nodatafound').text(ndf);

And that's it!

See a demonstration on this sample search page.
Note it refreshes the report on key release.
Try type letters not found in the emp table.