Skip to Main Content

Drag & Drop Interactive Grid rows

Here's an example that lets you drag and drop the rows of an interactive grid to arrange the records the way you want. A table used in an interactive grid must have a column that stores the sort order. If your table doesn't already have a column that can be used for this, edit the table and add a new column.

alter table <your table name> add (display_seq number);
update <your table name> set display_seq = rownum;
alter table <your table name> modify display_seq not null;

First, create an editable interactive grid and run the page. Sort the interactive grid in ascending order by display sequence column.

Then hide the display sequence column from the report.

Save the report.

Create new column to the interactive grid .

Enter a column name. Change the type to “HTML expression”. Add to the HTML Expression:

<span aria-hidden="true" class="fa fa-sort z-sortable--handle"></span>

Change the Source Type to None. Add a Column Initialization JavaScript Function:

sortableIG.initHandleColumn

Edit the other interactive grid columns and disable Sorting for all columns except the display sequence column.

Edit the display sequence column and add a Column Initialization JavaScript Function:

sortableIG.initSequenceColumn

Add to the interactive grid region Initialization JavaScript Function:

sortableIG.initRegion

 

Add to the page JavaScript Function and Global Variable Declaration:

// create a namespace for sortable IG
var sortableIG = {};

( function( $, region, sortableIG ) {

  // Set defaults
  sortableIG.options = {
    region: {
      defaultModelOptions: {
        sequenceStep: 10
      }
      ,defaultGridViewOptions: {
        reorderColumns: false
      }
    }
    ,handleColumn: {
      defaultGridColumnOptions: {
        noHeaderActivate: true
      }
      ,layout: {
        columnAlignment: "center"
        ,noStretch: true
      }
    }
    ,sequenceColumn: {
      features: {
      	sort: true
        ,canHide: false
        ,aggregate: false
        ,compute: false
        ,controlBreak: false
        ,groupBy: false
        ,highlight: false
        ,pivot: false
      }
    }
  }

  // sortable IG handle column Initialization code
  // call function in IG column Advanced: Column JavaScript Initialization Code
  sortableIG.initHandleColumn = function( options ) {

    return $.extend( true, options, sortableIG.options.handleColumn );

  }

  // sortable IG sequence column Initialization code
  // call function in IG column Advanced: Column JavaScript Initialization Code
  sortableIG.initSequenceColumn = function( options ) {

		// get sequence column name and store it for region Initialization JavaScript Function
    sortableIG.options.region.defaultModelOptions.sequenceField = options.name;

    return $.extend( true, options, sortableIG.options.sequenceColumn );

  }

  // sortable IG initialization code
  // call function in IG region Advanced: JavaScript Initialization Code
  sortableIG.initRegion = function( options ) {

    $( $x( options.regionStaticId ) ).on( "interactivegridviewchange interactivegridreportsettingschange", function( _event, ui ) {

      var view          = region( this.id ).call( "getViews", "grid" )
        , sortableArea$ = view.view$.find( ".a-GV-w-scroll tbody" )
        , helperBgClass = "u-color-29-bg"
      ;

      // https://api.jqueryui.com/sortable/
      sortableArea$.sortable({
        handle:       ".z-sortable--handle"
        ,items:       "tr:has(.z-sortable--handle)"
        ,axis:        "y"
        ,tolerance:   "pointer"
        ,scrollSpeed: 8
        ,placeholder: "a-GV-cell u-color-1-bg"
        
        ,helper: function( _event, ui ){
          ui.children().each( function(){
            $( this ).width( $( this ).width() ).addClass( helperBgClass )
          })
          return ui;
        }
        
        ,update: function( _event, ui ){
          
          var model = view.model
            , item$ = $( ui.item )
            // get moved record
            , item = ( item$.length ) ? model.getRecord( item$.data( "id" ) ) : null
            // get next or previous row from visible table depending is record moved up or down
            , after$ = ( ui.originalPosition.top > ui.position.top ) ? item$.next() : item$.prev()
            , after = ( after$.length ) ? model.getRecord( after$.data( "id" ) ) : null
          ;
          
          // remove helper background class, if row isn't actually moved
          item$.children().removeClass( helperBgClass );
          // check if we have found record and place where record is moved
          if( item && after ){
            // if record is moved upwards, we need get previous record from model
            after = ( ui.originalPosition.top > ui.position.top ) ? model.recordAt( model.indexOf( after ) - 1 ) : after;
            // move record
             if( item !== after ){
              model.moveRecords( [item], null, after );
            }
          }
          
        }
      
      });

    });

    return $.extend( true, options, sortableIG.options.region );

  }

})( apex.jQuery, apex.region, sortableIG );

Add to the Page CSS Inline:

.z-sortable--handle{
  cursor: move;
}
.ui-sortable-helper{
  display: inline-flex;
}

Now when you run a page, you can drag and drop to arrange the rows of the interactive grid from the handle column.

See working example in apex.oracle.com.

If you need support for touch events, you can try jQuery UI Touch Punch. Download jquery.ui.touch-punch.js and upload the file to Static Application Files. Reference the file e.g. on page JavaScript File URLs:

#APP_FILES#jquery.ui.touch-punch.js

Comments

  • Chris 21-AUG-23 06.51.29.211988 AM

    Thank you Jari. Works like a charm.