Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. 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!

Saturday, 21 September 2019

Change label dynamically in Oracle APEX

I was developing with Oracle Forms for an awesome project when I first heard about Twitter, and it was described to me by Jeff as a 'micro-blogging' site.

I think I did what could be my smallest blog post, as a tweet. A micro-blog, if you will.


Here is the snippet that dynamically updates a label, in this case, a floating label.
$("label[for='P1_NOTE']").text('Happy Friday!');

Which could also be written as
$("#P1_NOTE_LABEL").text('Happy Friday!');


This could be executed within a dynamic action, as a result of a change to some field on your page, perhaps to help instruct the user.

Happy APEXing!

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

Monday, 17 September 2018

Hide region if no data found

I have a diagnostic page where I hide classic report regions that aren't relevant - ie, have no data returned.

Here I create a dynamic action that executes After Refresh of the relevant region.

Dynamic Action definition

The client-side condition evaluates the presence of the .nodatafound class within a classic report, which is present only when no records are returned.

Inspect element of region with no records

The dynamic action then shows/hides the region, depending on the result of the JavaScript expression:
$(this.triggeringElement).find('.nodatafound').length == 1

The triggering element is the region, so find the class within that region, count the result set, and compare to value 1.


Instead of the region itself, this could relate to other components only relevant when records are returned.

As Maxime pointed out in the comments, here I was focussing on classic report regions. The relevant classes you may be looking for include:

  • Classic Report (versatile): nodatafound
  • Interactive Report (IR): a-IRR-noDataMsg
  • Interactive Grid (IG): a-GV-noDataMsg

This can mean there is no need for a server-side condition to test for existence.

With this dynamic action, the region could re-appear upon refresh with results, making the page more interactive without requiring full page submission.

Friday, 14 September 2018

Add record count to collapsed region

I have a diagnostics page where I wanted to display how many records are in a collapsible region's title.

The following is a simple solution, but will only work properly if all records are displayed, and no pagination is used.
Otherwise, check this past post for alternative methods to get the number of rows in a region.

Pick a column in the region and add a class. I chose "cnt".

Column attribute

To be selective, it's always handy to add a static ID to the region.

This is not an 'advanced' feature

Now we're ready to create an After Refresh Dynamic Action on the region, executing the following JavaScript, also firing on initialisation

$('#p99_share .t-Region-title').text('Sharing ('+$('#p99_share .cnt').length+')');

It will relabel region title with the amount of .cnt classes it could find in the region.

The final result looks like this:

Collapsible region with modified title

An alternative that skips the need for a static region ID would be

$(this.trigginerElement).find('.t-Region-title')
   .text('Sharing ('+$(this.trigginerElement).find('.cnt').length+')');

After Refresh Dynamic Action
Edit - Trent offered a more declarative example.

Relatively simple, but effective.


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.

Thursday, 17 August 2017

Revising jQuery with Oracle APEX

A few years ago I managed to bust out a book that explored my experiences using jQuery within Oracle APEX to solve various problems, many originally explored when building some "backend" apps for use on tablets.

For those who've read my book, thank you and I hope it was useful to you.
(I also hope you read the revised chapter 9 on calling PL/SQL from JavaScript.)
Please let me know what you thought of it with a review on Amazon.

The reason I bring this up is because I was asked on Twitter whether I would do a revision of my book for APEX 5.1.
I'm not sure if you've noticed, but my extra-curricular output has been down this year, despite my best intentions. That's not my answer, but it does weigh into what I think about the answer to that question.

Half of that book was written prior to APEX 5.0 being released. While a lot of the content is agnostic to the version of APEX you have, I must admit my techniques have evolved since the advent of the Page Designer.

That's not to say learning how to apply jQuery within APEX isn't useful. Having these fundamentals are still very much applicable, and useful to know when constructing dynamic actions; and even when defining CSS. But I think because it's much quicker to navigate through your dynamic actions in the Page Designer, I think there is less need to bundle logic within a JavaScript block.

I'm not sure revising this particular book for a later version of APEX would add value.

NASA Cassini
My video course had no particular theme and just explored various APEX techniques, hence the name. My book wasn't much different, and if I was going to do something again in future, it would possibly be along the same lines. Or something
even more applied. I do have ideas.

But I've been enjoying an awful lot of science in the time being. So much going on in space news!
It will be sad to see Cassini finish... Don't be surpised, I already have a post in mind for that amazing project. Correction, I already mentioned it during my 2016 blog review. Heck, I might do another anyway.

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, 29 March 2017

Make Radio Group look like UT Buttons

It's pretty easy to convert links in APEX reports to use the Universal Theme button look & feel, as I described here

http://www.grassroots-oracle.com/2015/12/tutorial-include-action-button-in-report.html

We can apply the same technique to radio groups, but it requires a little more work than just adding some classes, but nothing like jQuery mobile radio fiddles.

It took me a few goes to get the correct string in the correct APEX attribute to make the individual buttons look & behave the way I imagined, so I thought I'd share the journey. Show my working, so to speak.

First, create a radio item:

A humble radio group

Then set 'Option HTML attributes' to add template option classes, just like I did for the report action buttons. This applies the string to each option, rather than the entire item.

class="t-Button t-Button--simple"

If we check the page, the radio group should have button boxes surrounding the text labels.

Radio group with button class


To work out what CSS selector I needed to hide those radio buttons, I used Chrome's Inspect element tool (right click a radio button) so I can see how the HTML is constructed.

Inspect Element is the best browser tool. The best.

One shared property for each item is how the input tag has type=radio.
Further up you can see the entire fieldset as an id of P47_RADIO, our item.
Combine these to identify any radio buttons within this specific item.

Edit page 'Inline CSS' to using this combined CSS selector, with a property to hide any web component identified with that selector.

#P47_RADIO input[type=radio] {
  visibility:hidden; 
  position:absolute;
}

Edit: Commenter Yol had success with visibility:hidden instead of display:none, when it comes to submitting the values. This may be a browser dependent issue.
Hence I have also set position:absolute to ensure the radio buttons don't take up space, as per visibility attribute definition.


This tidies the button group, trouble is,  the current selection is unknown without the native radio button. This can be solved by creating a dynamic action that toggles button classes upon click of each radio item.

Create dynamic action on click of jQuery selector

#P47_RADIO span.t-Button

Add action to Execute JavaScript
// add simple class to all options
$('#P47_RADIO span.t-Button').addClass('t-Button--simple');
// remove from current selection
$(this.triggeringElement).removeClass('t-Button--simple');
// Ensure underlying radio selected
$(this.triggeringElement).prev().click();
The result:



Edit 2: I found that clicking in the green padding area did not change the radio selection, only when I clicked in the blue label. So I added the third line in the JavaScript to ensure the input value simulates the click event.


In my case the t-Button--simple template option is removed from the current selection, but you can play with these options to choose a combination that suits your desired contrast, using the various colours:

t-Button--danger
t-Button--warning
t-Button--success
t-Button--primary


Or "hotness", a highlight:
t-Button--hot

Or contrast, well, not highlighting the entire button:
t-Button--simple
All demonstrated in the button builder reference in the Universal Theme app.

You can achieve consistent button width by adding this to your Inline CSS:
#P47_RADIO .t-Button {width: 100px;}
After your page loads, you can use the inspect element tool and increase the width 'live', to find a figure/unit that works for you.

Now we need to ensure this selection is defaulted when the page opens. I prefer to execute the click() event as to simulate the actual action, as opposed to replicating the relevant addClass() function.

You can use a similar technique to conditionally hide a specific radio button in the group (though you still need to validate the availability to the user on the database end.)

Add this to your 'Execute when page loads' page attribute, instead of adding a Default Value the item itself.

$('#P47_RADIO input[value=P]').next().click()

Where the value=P represents the relevant radio item value. The .next() moves selection to the next sibling, which is the span element, where click is triggered.

Or you could consider this button group plugin.

Or in APEX 5.1 you can use the pill template option.

Or you could use some form of List template button group.

Or you can stick with tiny buttons only people using a mouse on a desktop PC can hope to aim for.

One of the beauties of Oracle APEX - so many options. I think I need to try convert this to a plugin.

Happy APEXing!

Tuesday, 11 October 2016

OTN Appreciation Day : APEX Dynamic Actions

I'm going to take advantage of the fact I live in a city so remote to many others in this amazing community, and schedule this post for my local 8am time. It's seems my schedule didn't work. Not the first fail from blogger... This might get me as one of the first posts in what's hopefully an interesting day amongst Oracle bloggers.

Tim Hall, a great producer of resources for Oracle technologists, suggested bloggers new and old post about their favourite feature, in appreciation for the professional network that is OTN.
https://oracle-base.com/blog/2016/09/28/otn-appreciation-day

I nominated APEX Dynamic Actions for sake of brevity in the subject line, and while they are pretty awesome as a whole, it does encompasses a range of sub-component features.

So I want to be more specific than that, I nominate the jQuery Selector attribute.

One of my favourite features
I could have selected one of a bunch of SQL functions (listagg, nvl2) or some PL/SQL features (%TYPE), or maybe something rarer like Oracle Text, but this one is a little more important to me.

For me it's a bridge between the Oracle world and the native web page we are generating.

With this attribute specifically, we can nominate, with precision, a component on the page and attach some event handler. We can make the web page communicate with the database.

Understanding how this feature operates I found like a gateway to building more interactive applications.

I looked back and it started with a post like this in 2012:
http://www.grassroots-oracle.com/2012/09/using-jquery-selector-in-apex.html
And took me so far as to write a book combining jQuery with APEX:
http://goo.gl/ZzQ0RP
(apparently chapter 9 has finally been corrected, more detail later)

Learning about this feature and further steering my career in an interesting direction required a community that blogged, participated in forums, tweeted, slacked, attended user group events, presented, networked, authored, and so on.

Giving also helps you learn. I do a little bit every now and then on the forums, and now I'm deemed a "legend". I read, watch interactions from interesting questions, and occasionally contribute. It helps in both directions, it's awesome, and it adds up. Same goes for presenting. A topic choice for next year is helping me learn what I need to keep my skills up to date.

#ThanksOTN

Wednesday, 21 September 2016

Show report tooltip as notification

Last week I described a method to make the tooltip on information more accessible to the end user.

Here is how you could make the same information available to touchscreen users.

1) First step, as before, is to define the HTML Expression of the column to include the title tag.
In this case I also stored my row identifier as an extra data- attribute.
<span title="#RECENT_NOTE#" data-key="#ROW_KEY#">#MY_COLUMN#</span>

2a) Create a dynamic action on click of the column cell.
jQuery Selector:
td[headers="my_column"]

2b) Set the value of a hidden page item.
This could be done a few ways, but here is how you can do it with Set Value, based on a JavaScript expression.
Set Value action example

What's pertinent is this.triggeringElement, which identifies the element being clicked. In this case it's the table cell, so .children() identifies the span from the HTML expression; .data() grabs the extra attribute; and .attr() grabs the title tooltip.
$(this.triggeringElement).children('span').data('key') + ': ' + $(this.triggeringElement).attr('title')

This expression extracts the a relevant key value (to identify the record) and concatenates it with the tooltip.

My P50_LAST_NOTE is hidden and unprotected.

2c) A Notification plugin could be used to display the value set in the hidden item, as a second action in the dynamic action. Other notification plugins are also available.
This 'gritter' style notification from APEX uses the hash syntax to get the client value,
#P50_LAST_NOTE#
As opposed to the value from session state while the page was rendered.
&P50_LAST_NOTE.
This information is documented in the relevant attribute help.

Now when you tap (or click) on the cell, the tooltip is displayed in the notification.