How to Add Rows to an Interactive Grid from a Popup Selection in Oracle APEX

Build a multi-select popup with a row selector, return the ticked rows with Close Dialog, and insert or update grid rows through apex.model.

Typing detail rows one by one is slow when users already know what they want. They would rather open a list, tick several entries and get one new grid row for each of them. The rows should arrive with the right defaults, such as the current price and a quantity of 1, ready to change and save.

Oracle APEX can do this with a modal dialog page that has a selectable Interactive Grid. The dialog returns the ticked rows to the parent page, and a few lines of JavaScript insert them into the parent grid through its model. This guide builds both pages, handles items that are already on the list, and tests the whole flow.

The guide How to Return Values from a Modal Dialog to the Parent Page in Oracle APEX returns one value into page items. The older post on inserting Interactive Grid records with JavaScript adds rows from page items. This guide joins both ideas: a multi-select popup that fills a grid.

The example uses purchase requests and supply items; the same steps work for any parent and detail tables.

Step 1: Create the Tables

Three tables hold the data:

  • SUPPLY_ITEMS is the list users pick from, with a code, a name, a category and a unit price.
  • PURCHASE_REQUESTS has one row per request.
  • PURCHASE_REQUEST_LINES has one row per item on a request, with the quantity and the unit price.

Example:

set define off

create table supply_items (
    item_id     number generated by default on null as identity,
    item_code   varchar2(20)  not null,
    item_name   varchar2(80)  not null,
    category    varchar2(30)  not null,
    unit_price  number(10,2)  not null,
    is_active   varchar2(1)   default on null 'Y' not null,
    constraint supply_items_pk primary key (item_id),
    constraint supply_items_code_uk unique (item_code),
    constraint supply_items_price_ck check (unit_price > 0),
    constraint supply_items_active_ck check (is_active in ('Y', 'N'))
);

create table purchase_requests (
    request_id     number generated by default on null as identity,
    request_title  varchar2(100) not null,
    requested_by   varchar2(60)  default on null
                   coalesce(sys_context('APEX$SESSION', 'APP_USER'), sys_context('USERENV', 'SESSION_USER')) not null,
    request_date   date default on null sysdate not null,
    constraint purchase_requests_pk primary key (request_id)
);

create table purchase_request_lines (
    line_id     number generated by default on null as identity,
    request_id  number not null,
    item_id     number not null,
    quantity    number(6)    default on null 1 not null,
    unit_price  number(10,2) not null,
    amount      number(12,2) generated always as (quantity * unit_price) virtual,
    constraint purchase_request_lines_pk primary key (line_id),
    constraint purchase_request_lines_req_fk foreign key (request_id)
        references purchase_requests (request_id) on delete cascade,
    constraint purchase_request_lines_item_fk foreign key (item_id)
        references supply_items (item_id),
    constraint purchase_request_lines_uk unique (request_id, item_id),
    constraint purchase_request_lines_qty_ck check (quantity > 0),
    constraint purchase_request_lines_price_ck check (unit_price >= 0)
);

create index purchase_request_lines_item_ix on purchase_request_lines (item_id);

insert into supply_items (item_code, item_name, category, unit_price) values ('PAP-A4', 'A4 Copy Paper (500 sheets)', 'Paper', 6.50);
insert into supply_items (item_code, item_name, category, unit_price) values ('PAP-NOTE', 'Sticky Notes Pack', 'Paper', 3.20);
insert into supply_items (item_code, item_name, category, unit_price) values ('WRT-PEN', 'Blue Ballpoint Pens (10)', 'Writing', 4.75);
insert into supply_items (item_code, item_name, category, unit_price) values ('WRT-MRK', 'Whiteboard Markers (4)', 'Writing', 5.90);
insert into supply_items (item_code, item_name, category, unit_price) values ('DSK-STP', 'Stapler', 'Desk', 12.00);
insert into supply_items (item_code, item_name, category, unit_price) values ('DSK-TAPE', 'Tape Dispenser', 'Desk', 7.40);
insert into supply_items (item_code, item_name, category, unit_price) values ('TEC-MOU', 'Wireless Mouse', 'Tech', 18.99);
insert into supply_items (item_code, item_name, category, unit_price) values ('TEC-USB', 'USB-C Hub', 'Tech', 34.50);
insert into supply_items (item_code, item_name, category, unit_price) values ('TEC-HDS', 'Headset with Microphone', 'Tech', 42.00);
insert into supply_items (item_code, item_name, category, unit_price, is_active) values ('TEC-FAX', 'Fax Machine Toner', 'Tech', 55.00, 'N');

insert into purchase_requests (request_title, requested_by) values ('Office restock - October', 'DEMO_DEV');
insert into purchase_requests (request_title, requested_by) values ('New starter desk kit', 'DEMO_DEV');

insert into purchase_request_lines (request_id, item_id, quantity, unit_price)
select 1, item_id, 5, unit_price from supply_items where item_code = 'PAP-A4';
insert into purchase_request_lines (request_id, item_id, quantity, unit_price)
select 1, item_id, 2, unit_price from supply_items where item_code = 'WRT-PEN';

commit;

select r.request_id, r.request_title, i.item_name, l.quantity, l.unit_price, l.amount
from purchase_requests r
left join purchase_request_lines l on l.request_id = r.request_id
left join supply_items i on i.item_id = l.item_id
order by r.request_id, l.line_id;

Output:

Table SUPPLY_ITEMS created.

Table PURCHASE_REQUESTS created.

Table PURCHASE_REQUEST_LINES created.

Index PURCHASE_REQUEST_LINES_ITEM_IX created.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

1 row inserted.

Commit complete.

   REQUEST_ID REQUEST_TITLE               ITEM_NAME                        QUANTITY    UNIT_PRICE    AMOUNT
_____________ ___________________________ _____________________________ ___________ _____________ _________
            1 Office restock - October    A4 Copy Paper (500 sheets)              5           6.5      32.5
            1 Office restock - October    Blue Ballpoint Pens (10)                2          4.75       9.5
            2 New starter desk kit
  • PURCHASE_REQUEST_LINES_REQ_FK links each line to its request and deletes the lines with the request. PURCHASE_REQUEST_LINES_ITEM_FK links each line to a supply item, and PURCHASE_REQUEST_LINES_ITEM_IX indexes that foreign key.
  • PURCHASE_REQUEST_LINES_UK allows each item only once per request. The popup code below adds 1 to the quantity instead of adding a second line.
  • AMOUNT is a virtual column (quantity times unit price), so no trigger or process has to keep it up to date.
  • QUANTITY, IS_ACTIVE, REQUESTED_BY and REQUEST_DATE use DEFAULT ON NULL, so the database fills them without a trigger.
  • The Fax Machine Toner item is inactive. The popup lists only active items.

Step 2: Create the Request Lines Page

In App Builder, open the application and click Create Page. On the Component tab, select Interactive Grid:

Create a Page wizard with the Interactive Grid tile marked with a red box
Choose the Interactive Grid page type.

Set Page Number to 930 and Name to Request Lines. Keep Page Mode Normal:

Create Interactive Grid screen with Page Number 930 and Name Request Lines marked with a red box
The page number and name.

Under Data Source, keep Source Type Table, set Table / View Name to PURCHASE_REQUEST_LINES and turn on Editing Enabled:

Data Source section with Table / View Name PURCHASE_REQUEST_LINES and Editing Enabled on, marked with a red box
An editable grid on the lines table.

Keep the Navigation settings. The wizard adds a breadcrumb and a navigation menu entry:

Navigation section with Use Breadcrumb and Use Navigation on
The default navigation settings.

Click Next. On the Primary Key screen, select LINE_ID and click Create Page:

Primary Key screen with Primary Key Column 1 LINE_ID marked with a red box
LINE_ID is the primary key.

Step 3: Set the Grid Columns

In this example, APEX 26.1 created every column of the grid as a Select List based on the request titles, also QUANTITY, UNIT_PRICE and AMOUNT. Check the column types after the wizard and change them as shown below.

In Page Designer, expand Request Lines and Columns. Select LINE_ID and set Type to Hidden:

Page Designer with column LINE_ID selected and Type Hidden marked with a red box
The primary key column stays hidden.

Select REQUEST_ID and set Type to Hidden:

Column REQUEST_ID with Type Hidden marked with a red box
REQUEST_ID is hidden too.

Under Default, set Type to Item and Item to P930_REQUEST_ID. You create this item in Step 4. Every new row then belongs to the request chosen on the page:

Default group of REQUEST_ID with Type Item and Item P930_REQUEST_ID marked with a red box
New rows take the request from the page item.

Select ITEM_ID. Keep Type Select List and set Heading to Item:

Column ITEM_ID with Type Select List and Heading Item marked with red boxes
The item column.

Under List of Values, set Type to SQL Query, enter this query, and set Null Display Value to - Select Item -:

Example:

select item_name as display_value,
       item_id as return_value
from supply_items
order by item_name
List of Values group of ITEM_ID with Type SQL Query, the query and Null Display Value marked with red boxes
The item list includes inactive items, so old lines still show their names.

Select QUANTITY and set Type to Number Field:

Column QUANTITY with Type Number Field marked with a red box
Quantity is a number field.

Select UNIT_PRICE, set Type to Number Field and Format Mask to FM999G990D00:

Column UNIT_PRICE with Type Number Field and Format Mask FM999G990D00 marked with red boxes
The price shows two decimals.

Select AMOUNT, set Type to Display Only, turn on Query Only under Source, and set Format Mask to FM999G990D00. The database computes this virtual column, so APEX must never try to write it:

Column AMOUNT with Type Display Only and Query Only on, marked with red boxes
AMOUNT is read-only.

Step 4: Add the Request Select List and the Add Items Button

In the Body of page 930, create a region above the grid. Set Name to Purchase Request and Type to Static Content. Then create a page item in this region.

Set Name to P930_REQUEST_ID, Type to Select List, Label to Request, and Page Action on Selection to Submit Page:

Page item P930_REQUEST_ID with Type Select List and Page Action on Selection Submit Page marked with red boxes
Choosing another request reloads the page.

Under List of Values, set Type to Shared Component and List of Values to PURCHASE_REQUESTS.REQUEST_TITLE. The wizard created this list of values. Turn off Display Null Value, so a request is always selected:

List of Values group with Type Shared Component, List of Values PURCHASE_REQUESTS.REQUEST_TITLE and Display Null Value off, marked with red boxes
The request list.

Under Default, set Type to SQL Query (return single value) and enter select min(request_id) from purchase_requests:

Default group with Type SQL Query (return single value) and the query marked with a red box
The page opens with the first request.

Create a button in the Next slot of the Purchase Request region. Set Button Name to ADD_ITEMS and Label to Add Items. Under Behavior, set Action to Redirect to Page in this Application and Target to page 931, which you create in Step 6:

Button ADD_ITEMS with Label Add Items, Action Redirect to Page in this Application and Target Page 931 marked with red boxes
The button opens the popup page.

Page 931 is a modal dialog, so the redirect opens it as a popup over page 930. In this example, the button also uses the Text with Icon template, the icon fa-plus and the template option Icon Left.

Step 5: Filter and Prepare the Grid

Select the Request Lines region. Under Source, set Where Clause to request_id = :P930_REQUEST_ID, Order By Clause to line_id, and Page Items to Submit to P930_REQUEST_ID:

Source group of the Request Lines region with Where Clause, Order By Clause and Page Items to Submit marked with a red box
The grid shows only the lines of the chosen request.

Under Advanced, set HTML DOM ID to request_lines. The JavaScript in Step 10 finds the grid by this ID:

Advanced group with HTML DOM ID request_lines marked with a red box
The DOM ID of the grid.

Open the Attributes tab. Turn off Add Row If Empty and Select First Row:

Attributes tab with Add Row If Empty off and Select First Row off, marked with red boxes
No empty starter row and no preselected row.

With Add Row If Empty on, a request without lines showed one blank row. That blank row stayed under the picked rows, which looked odd, so this guide turns it off.

Step 6: Create the Select Supply Items Popup Page

Click Create Page again and select Interactive Grid on the Component tab. Set Page Number to 931, Name to Select Supply Items, and Page Mode to Modal Dialog:

Create Interactive Grid screen with Page Number 931, Name Select Supply Items and Page Mode Modal Dialog marked with a red box
The popup is a modal dialog page.

Under Data Source, set Source Type to SQL Query and enter this query. Keep Editing Enabled off, then click Create Page:

Example:

select item_id,
       item_code,
       item_name,
       category,
       unit_price
from supply_items
where is_active = 'Y'
order by category, item_name
Data Source section with Source Type SQL Query and the supply items query marked with a red box
A read-only grid of active items.

For a modal dialog, the wizard turns off the breadcrumb and the navigation entry, and the Create Page button appears on the first screen.

Step 7: Set Up the Selectable Grid

In Page Designer of page 931, select the Select Supply Items region. Under Advanced, set HTML DOM ID to supply_items_grid:

Advanced group of the Select Supply Items region with HTML DOM ID supply_items_grid marked with a red box
The DOM ID of the picker grid.

On the Attributes tab, turn off Select First Row. Under Toolbar, untick Actions Menu and Save Button. Users only search and tick rows here:

Attributes tab with Select First Row off and the toolbar Controls without Actions Menu and Save Button, marked with red boxes
With Select First Row on, the first item was already ticked when the popup opened.

A read-only grid has no checkbox column by default. Right-click Columns and select Create Column:

Context menu of Columns with Create Column marked with a red box
Add a column for the checkboxes.

Set the new column's Column Name to APEX$ROW_SELECTOR and Type to Row Selector. Keep Enable Multi Select and Show Select All on:

Column APEX$ROW_SELECTOR with Type Row Selector, Enable Multi Select and Show Select All on, marked with red boxes
The row selector adds a checkbox to each row.

Select ITEM_ID, set Type to Hidden and turn on Primary Key under Source:

Column ITEM_ID with Type Hidden and Primary Key on, marked with red boxes
The item ID is hidden but still in each record.

Select UNIT_PRICE and set Format Mask to FM999G990D00:

Column UNIT_PRICE with Format Mask FM999G990D00 marked with a red box
The same price format as in the parent grid.

In this example, the grid's Heading also has Fixed To Region with a report height of 420 pixels, so all nine items fit without scrolling. Under Dialog on the page itself, Width is 800.

Step 8: Add the Hidden Item and the Buttons

In the Dialog Footer, create a region. Set Name to Buttons, Type to Static Content and Template to Buttons Container. In this region, create a page item P931_SELECTED with Type Hidden and turn off Value Protected:

Page item P931_SELECTED with Type Hidden and Value Protected off, marked with red boxes
JavaScript writes the selection into this item.

Value Protected must be off because JavaScript sets the item. The item is never submitted; the dialog only returns its value to page 930.

Create a button CANCEL in the Close slot of the Buttons region. Set Action to Trigger Action, then under the button add the action Cancel Dialog:

Button CANCEL with Action Trigger Action marked with a red box
Cancel closes the popup without returning anything.

Create a button in the Next slot. Set Button Name to ADD_SELECTED, Label to Add Selected, turn on Hot, and set Action to Defined by Dynamic Action:

Button ADD_SELECTED with Label Add Selected and Action Defined by Dynamic Action marked with red boxes
A dynamic action handles this button.

Step 9: Return the Ticked Rows from the Popup

On the Dynamic Actions tab, create a dynamic action. Set Name to Return Selected Items, Event to Click, Selection Type to Button and Button to ADD_SELECTED:

Dynamic action Return Selected Items with Event Click, Selection Type Button and Button ADD_SELECTED marked with red boxes
The dynamic action fires when the user clicks Add Selected.

Under Client-side Condition, set Type to JavaScript expression and enter this expression:

Example:

apex.region("supply_items_grid").call("getSelectedRecords").length > 0
Client-side Condition with Type JavaScript expression and the expression marked with a red box
True actions run only when at least one row is ticked.

Set the first True action to Execute JavaScript Code with this code:

Example:

var grid = apex.region("supply_items_grid");
var model = grid.call("getViews", "grid").model;
var picked = grid.call("getSelectedRecords").map(function (record) {
    return {
        id: model.getValue(record, "ITEM_ID"),
        name: model.getValue(record, "ITEM_NAME"),
        price: model.getValue(record, "UNIT_PRICE")
    };
});
apex.item("P931_SELECTED").setValue(JSON.stringify(picked));
True action with Action Execute JavaScript Code and the code marked with red boxes
The code collects the ticked rows.

getSelectedRecords returns the ticked records of the grid. The code reads ITEM_ID, ITEM_NAME and UNIT_PRICE from each record through the grid model and stores the list as JSON text in P931_SELECTED.

Add a second True action with Action Close Dialog and set Items to Return to P931_SELECTED:

True action with Action Close Dialog and Items to Return P931_SELECTED marked with red boxes
The dialog returns the JSON text to page 930.

Add a False action with Action Alert and Message Select at least one item.:

False action with Action Alert and Message Select at least one item. marked with red boxes
The message when nothing is ticked.

Under Execution, Fire When Event Result Is shows False for this action:

Execution group with Fire When Event Result Is False marked with a red box
The alert is a False action.

Step 10: Insert the Rows into the Parent Grid

Go back to page 930. On the Dynamic Actions tab, create a dynamic action. Set Name to Add Picked Items, Event to Dialog Closed, Selection Type to Button and Button to ADD_ITEMS:

Dynamic action Add Picked Items with Event Dialog Closed, Selection Type Button and Button ADD_ITEMS marked with red boxes
The dynamic action fires when the popup closes with a result.

Set its True action to Execute JavaScript Code with this code:

Example:

var picked = JSON.parse(this.data.P931_SELECTED || "[]");
var model = apex.region("request_lines").call("getViews", "grid").model;
var lastAdded = null;
function plain(v) {
    return (v !== null && typeof v === "object") ? v.v : v;
}
picked.forEach(function (item) {
    var existing = null;
    model.forEach(function (record, index, id) {
        var meta = model.getRecordMetadata(id);
        if (!meta.deleted && String(plain(model.getValue(record, "ITEM_ID"))) === String(item.id)) {
            existing = record;
        }
    });
    if (existing) {
        var qty = Number(model.getValue(existing, "QUANTITY")) || 0;
        model.setValue(existing, "QUANTITY", String(qty + 1));
    } else {
        var newId = model.insertNewRecord(null, lastAdded);
        var record = model.getRecord(newId);
        lastAdded = record;
        model.setValue(record, "ITEM_ID", { v: String(item.id), d: item.name });
        model.setValue(record, "QUANTITY", "1");
        model.setValue(record, "UNIT_PRICE", String(item.price));
    }
});
True action with Action Execute JavaScript Code and the code marked with red boxes
The code inserts or updates grid rows.

this.data holds the items the dialog returned, so this.data.P931_SELECTED is the JSON text from Step 9. The code gets the model of the grid with the DOM ID request_lines and goes through the picked items one by one.

For each item, model.forEach looks for a row that already has this ITEM_ID and is not deleted. If one exists, the code adds 1 to its quantity. Otherwise, insertNewRecord adds a row, and setValue fills the item, a quantity of 1 and the unit price.

A select list column holds its value as an object with v (value) and d (display text), so the code passes { v: id, d: name } and compares only v. The second argument of insertNewRecord puts each new row after the one added before it, so the rows keep the order of the popup.

REQUEST_ID is not set in the code. The column default from Step 3 fills it from P930_REQUEST_ID when the row is inserted.

Step 11: Stop Duplicate Items Added by Hand

Users can still click Add Row and choose an item that is already on the request. On the Processing tab of page 930, create a validation. Set Name to Item Not Already on Request, Editable Region to Request Lines, Type to No Rows returned, and enter this query:

Example:

select 1
from purchase_request_lines
where request_id = :REQUEST_ID
and item_id = :ITEM_ID
and line_id <> nvl(:LINE_ID, -1)
Validation Item Not Already on Request with Editable Region Request Lines, Type No Rows returned and the query marked with red boxes
A row validation on the grid.

Set Error Message to This item is already on the request. Change the quantity of the existing line instead. and Associated Column to ITEM_ID:

Error group with the error message and Associated Column ITEM_ID marked with red boxes
The message appears at the item cell.

The query looks for another saved line of the same request with the same item. The unique constraint PURCHASE_REQUEST_LINES_UK stays as the final guard.

Step 12: Test the Popup Selection

Run page 930 as DEMO_DEV. The page opens with the request Office restock - October and its two sample lines:

Request Lines page with two lines and the Add Items button marked with a red box
The starting point.

Click Add Items. Tick Stapler, Sticky Notes Pack and Wireless Mouse, and click Add Selected:

Select Supply Items popup with three ticked rows and the Add Selected button marked with red boxes
Three items ticked in the popup.

The popup closed and the grid got three new rows, with quantity 1 and the unit price of each item. They are not saved yet, so APEX marks them as new and AMOUNT is still empty:

Request Lines grid with three new unsaved rows Stapler, Sticky Notes Pack and Wireless Mouse marked with red boxes
The picked items are now grid rows.

Click Save. After the save, the grid shows AMOUNT from the virtual column:

Grid after saving, with the three new rows and their amounts marked with red boxes
The rows are saved.

The table now has the three new lines of request 1:

Example:

select l.line_id, l.request_id, i.item_name, l.quantity, l.unit_price, l.amount
from purchase_request_lines l
join supply_items i on i.item_id = l.item_id
where l.request_id = 1
order by l.line_id;

Output:

   LINE_ID    REQUEST_ID ITEM_NAME                        QUANTITY    UNIT_PRICE    AMOUNT
__________ _____________ _____________________________ ___________ _____________ _________
         1             1 A4 Copy Paper (500 sheets)              5           6.5      32.5
         2             1 Blue Ballpoint Pens (10)                2          4.75       9.5
        45             1 Stapler                                 1            12        12
        46             1 Sticky Notes Pack                       1           3.2       3.2
        47             1 Wireless Mouse                          1         18.99     18.99

Next, open the popup again and tick A4 Copy Paper, Stapler and Whiteboard Markers. The first two are already on the request:

Grid with Whiteboard Markers as a new row and Stapler and A4 Copy Paper with higher quantities, marked with red boxes
Existing items get a higher quantity instead of a second row.

Stapler went from 1 to 2 and A4 Copy Paper from 5 to 6, and only Whiteboard Markers became a new row. AMOUNT of the changed rows updates after the save:

Example:

select l.line_id, l.request_id, i.item_name, l.quantity, l.unit_price, l.amount
from purchase_request_lines l
join supply_items i on i.item_id = l.item_id
where l.request_id = 1
order by l.line_id;

Output:

   LINE_ID    REQUEST_ID ITEM_NAME                        QUANTITY    UNIT_PRICE    AMOUNT
__________ _____________ _____________________________ ___________ _____________ _________
         1             1 A4 Copy Paper (500 sheets)              6           6.5        39
         2             1 Blue Ballpoint Pens (10)                2          4.75       9.5
        45             1 Stapler                                 2            12        24
        46             1 Sticky Notes Pack                       1           3.2       3.2
        47             1 Wireless Mouse                          1         18.99     18.99
        48             1 Whiteboard Markers (4)                  1           5.9       5.9

6 rows selected.

Clicking Add Selected with no ticked row showed the alert, and the popup stayed open:

Popup with the alert Select at least one item.
Nothing is returned without a selection.

Cancel closed the popup and left the grid unchanged. Choosing New starter desk kit in the Request list reloaded the page with that request. Four picked items were saved with REQUEST_ID 2, which came from the column default.

Finally, click Add Row, choose Stapler by hand, enter a price and click Save. The validation stopped the save:

Error notification This item is already on the request, marked with a red box, and the Stapler cell in error
A duplicate added by hand is refused.

Things to Know

  • The validation checks saved lines only. When two new rows with the same item were saved together, it let both pass, and the unique constraint stopped the save with ORA-00001. Nothing was saved. The popup never creates such rows, because it raises the quantity instead.
  • The price comes from the popup at the time of picking. Users can still change it in the grid, which suits requests and quotes. If the price must always match the item list, make UNIT_PRICE read-only and set it on the server.
  • Choosing another request submits the page, and the page submit also saves pending grid changes. In a test, an unsaved new row was saved to the request shown before the switch.
  • Check the column types after the Interactive Grid wizard. In this example, the wizard turned all number columns into select lists based on the first foreign key.
  • The row selector works on a read-only grid. Select First Row must be off on the popup grid, or the first item is already ticked.

Related Guides

Conclusion

To add rows to an Interactive Grid from a popup selection in Oracle APEX, give the popup a read-only grid with a row selector, return the ticked rows as JSON with Close Dialog, and insert them through the parent grid's model in a Dialog Closed dynamic action.

In this example, three picked items became three saved lines with their prices. Items already on the request got a higher quantity instead of a second line. An empty selection, Cancel and a duplicate added by hand were all handled without bad data.

Get more Vinish.dev tutorials on Google

See my articles more often when you search Google.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted