IG - how to register changes to cell being edited - eg. to action shortcut key (Oracle Apex Interactive Grid)

370 views Asked by At

We have common functionality across multiple pages: Cancel/Save/Back/Next (buttons).

I added shortcut keys via app-level static JavaScript file: Alt-C/S/B/N (using common CSS on the buttons & jQuery binds).

Problem: when using shortcut key whilst editing a cell, the edits to the cell were not saved with Alt-S, Alt-N. (For same reason, no confirmation requested on Cancel/Back, since the grid did not see any changes.)

Much searching and reading of documentation did not reveal the answer...

After a huge amount of effort: inspecting of widget/ interactiveGrid/ model/ getCurrentView via the console & inspect-panes (partly because I didn't find/inspect the .interactiveGrid('getViews','grid).getColumns() for a long time), I found a solution, but I feel there must be a more elegant one. Advice sought! TIA.

1

There are 1 answers

3
Clark On

Here is my solution. Note the namespace is lps, and I have aliased apex.jQuery as lps.$ prior to the code below. I include comments for my Oracle colleagues less experienced with JS, Apex, jQuery, etc.

The solution works for all 4 shortcut keys, and should work for any IG (possibly even without a static id on the containing region, although my test case has a static id). There is some supporting code after the main solution. All happening on page load via the static JS file.

The jQuery selectors must only bind to a single button on each page!

lps.$(window).on('keydown',function($evt) // Capture Alt + shortcut key in keydown event
{
    // $evt is the jQuery simplified event

    if( $evt.altKey ) // the alt-key is being pressed-and-held
    {
        var key = $evt.key.toUpperCase();

        if( lps.shortcuts[key] )
        {
            if( lps.$($evt.target).is('input,textarea') ) // effect the onchange() JavaScript (more specifically, the IG version thereof)
            {
                // We've pressed a shortcut key combo, whilst editing a cell, so the IG-onchange-y event hasn't yet fired

                var tgtEle = $evt.target,
                    newVal = tgtEle.value, // the value of the on-screen element, regardless of change-status
                    tgtId  = tgtEle.id, // this tallies with an elementId exposed via the igCols below
                    activeFld;

                // IG stuff - igId - get the IG overall id via an IG ancestor div of the target field, then losing the trailing _ig suffix
                var igId    = lps.$(tgtEle).parents('div.a-IG')[0].id.replace(/_ig$/,'')
                    igWidget= apex.region(igId).widget(),
                    igGrid  = igWidget.interactiveGrid('getViews','grid'),
                    igModel = igGrid.model,
                    igView  = igWidget.interactiveGrid('getCurrentView'),
                    igRecId = igView.getActiveRecordId(),
                    igCols  = igGrid.getColumns(); // this provides meta information about the columns in the IG

                for( var i=-1; ++i<igCols.length; ) // find the field name of this IG-column from target element id
                {
                    if( igCols[i].elementId === tgtId )
                    {
                        activeFld = igCols[i].property;
                        break;
                    }
                }

                // Phew. Eventually setValue on the IG-cell so Apex knows the cell has changed without an onchange firing
                igModel.setValue(igModel.getRecord(igRecId),activeFld,newVal);
            }
            $evt.originalEvent.preventDefault(); // we're consuming the event, so stop it doing some native browser trick we don't want
            lps.$(lps.shortcuts[key].selector).select();  // select the to-be-clicked-element
            lps.$(lps.shortcuts[key].selector).click();   // synthesise a click on the selector'd element
        }
    }
});

Supporting code to pre-establish what our 4 shortcut keys are to be:

lps.shortcuts = { // keys and their jQuery selectors
    B : { selector:'button.nav-back' },
    C : { selector:'button.nav-cancel' },
    N : { selector:'button.nav-next' },
    S : { selector:'button.nav-save' },
};

Code to add hover-title to the buttons:

    // Advertise the shortcut with a 'title' attribute (appears as popup tip when hover over the element)
    for( var key in lps.shortcuts ){
        lps.$(lps.shortcuts[key].selector).attr('title','Alt+'+key);
    }