At this point we have a working report that links to a form. The report is based on a view and the form is based on a procedure. At this point the form is only loading the record in using a procedure that uses a record in its signature. In this post we’ll complete the functionality by using the same form for insert, update, and delete functionality.
Let’s start with adding a mode page item to the form. We will use this to know if the form is invoked in insert or in update/delete mode.
You now have a new page item which is not displayed, but whose value you can reference on this page.
To make this example easy, we’ll start with just implementing update functionality. But first we’ll go to the report and change this link column to pass in a “U” for the mode page item when it is clicked.
In the page with the report, perform the following steps.
Now with that done, let’s head back to the page with the form. But before we make any changes there, we need to change the package to add a procedure for updating a row in the emp table.
Here is the code for the procedure we’ll use.
create or replace package tb_access as type t_emp_rec is record (empno emp.empno%TYPE ,ename emp.ename%TYPE); procedure read_emp(p_in_empno in emp.empno%type ,p_out_emp_rec out t_emp_rec); procedure write_emp(p_in_empno in emp.empno%type ,p_in_emp_rec in t_emp_rec); end tb_access; create or replace package body tb_access as procedure read_emp(p_in_empno in emp.empno%type ,p_out_emp_rec out t_emp_rec) is begin select empno ,ename into p_out_emp_rec.empno ,p_out_emp_rec.ename from emp where empno = p_in_empno; end read_emp; ---------------------------------------------------- procedure write_emp(p_in_empno in emp.empno%type ,p_in_emp_rec in t_emp_rec) is begin update emp set ename = p_in_emp_rec.ename where empno = p_in_empno; end write_emp; end tb_access;
This code adds the procedure write_emp wich also takes empno and the record used for read_emp, only this time they are both in parameters as we’ll not return any data from the procedure that writes to the table. The actual code in the procedure is a very simple update that sets the name to the ename in the record for the row that has the empno passed in.
With this in place we’re ready to change the form to call this procedure upon submit. We do this by taking the code used for reading in the record (The process in page rendering) and put it into the “Run Stored Procedure” process we commented out in page processing.
The code we copy looks like this:
declare in_emp_rec tb_access.t_emp_rec; begin tb_access.read_emp(p_in_empno => :p4_empno ,p_out_emp_rec => in_emp_rec); :p4_ename := in_emp_rec.ename; end;
Replace the code in “Run Stored Procedure” with the above and change it to look like this:
declare in_emp_rec tb_access.t_emp_rec; begin in_emp_rec.ename := :p4_ename; tb_access.write_emp(p_in_empno => :p4_empno ,p_out_emp_rec => in_emp_rec); end;
In the “Run Stored Procedure” change the condition to “- Select condition type -” to not have a condition for when the process is executed. It is currently conditioned on running only when the save button is clicked, leave that condition on for now.
With that in place, it is time to test the functionality of the forms new functionality. Run the report, click on the link and update the name of an employee. Assuming you followed the above and got the changes made in the right places, the form will now update the name of the employee.
The next step in this will be to add insert functionality to the form. First off is to set up the package to support inserting an employee. As we’re now going to have both insert and update through the same procedure, we need to include the mode in the signature of the procedure.
Let’s first update the package specification so the write_emp looks like this:
procedure write_emp(p_in_empno in emp.empno%type ,p_in_mode in varchar2 ,p_in_emp_rec in t_emp_rec);
The procedure in the package needs to be updated to look like this:
procedure write_emp(p_in_empno in emp.empno%type ,p_in_mode in varchar2 ,p_in_emp_rec in t_emp_rec) is begin case p_in_mode when 'U' then update emp set ename = p_in_emp_rec.ename where empno = p_in_empno; when 'I' then insert into emp (empno ,ename) values((select max(empno) + 1 from emp) ,p_in_emp_rec.ename); end case; end write_emp;
Before the form is functional again, we need to change the call in “Run Stored Procedure” to match the new signature of write_emp.
declare in_emp_rec tb_access.t_emp_rec; begin in_emp_rec.ename := :p4_ename; tb_access.write_emp(p_in_empno => :p4_empno ,p_in_mode => :p4_mode ,p_in_emp_rec => in_emp_rec); end;
With that change in place the form now works and the mode we set on the link column is now passed to the procedure to make sure an update is performed and not an insert.
To introduce insert functionality, we want to add a new button on the page with the report that invokes the form in insert mode. To add the button follow these steps on the page with the report.
If you test the page, you’ll see that it ends up immediately to the right of the search bar. Do not click on the button just yet.
Now go to the page with the form and open the process in rendering (Read Emp).
This ensures that we’re only reading a row from emp when updating data. When inserting, there is no data to be read and displayed.
You may also want to set the same condition on the page item for empno to hide it when inserting as there is no empno to display then.
Test the function now by running the report page and click on the “Add” button. It adds a row with the name you enter on the form. The rest of the columns will have null as their value. Adding more columns in the procedures and on the form is an exercise left to the reader.
With the function now supporting insert and update, it is time to add the delete function so we can get rid of some of the test employees we’ve created. This function will be implemented only on the form and by adding supporting code in the package. Thus, the delete will be performed by clicking on the link and then clicking on a delete button on the form.
To implement this we’ll update the implementation of the write_emp procedure once more.
procedure write_emp(p_in_empno in emp.empno%type ,p_in_mode in varchar2 ,p_in_emp_rec in t_emp_rec) is begin case p_in_mode when 'I' then insert into emp (empno ,ename) values((select max(empno) + 1 from emp) ,p_in_emp_rec.ename); when 'U' then update emp set ename = p_in_emp_rec.ename where empno = p_in_empno; when 'D' then delete from emp where empno = p_in_empno; end case; end write_emp;
With the package updated, our next step is to change the code in the “Run Stored Procedure” process on the form page.
declare in_emp_rec tb_access.t_emp_rec; v_mode varchar2(1); begin in_emp_rec.ename := :p4_ename; v_mode := :p4_mode; if :REQUEST = 'DELETE' then v_mode := 'D'; end if; tb_access.write_emp(p_in_empno => :p4_empno ,p_in_mode => v_mode ,p_in_emp_rec => in_emp_rec); end;
The code is now changed such that a click on the delete button (not implemented yet) will set the mode to D. This is done by checking the REQUEST which we will set to DELETE for the button we’ll create now. REQUEST is a standard attribute available on buttons and branches in APEX. By setting it to a unique value, we can check for different invocation methods in code. This allows us to deal with different scenarios with just one block of code.
To add the button, follow these steps.
The value of “Button Name” is used to set REQUEST when the page is submitted. With this in place our form should now support deleting rows also. Update the branch on the page to not have a condition on the button pressed. Take it out for a spin.
There is one thing left to do, hide the delete button when the form is invoked to add a row.
Now you’ve got a CRUD solution based on an interactive report with a form that supports insert, Update, and Delete. The total amount of code required is very small and you still have isolated the APEX-application from changes to the table structure by using a view, even the record can have fields added to it without impacting the functionality of your application. Developing using the demonstrated methods here allows you to create these applications almost as fast as with making them table based, but you have much more control and can make a lot of changes without touching the application,