As the title states, we’re going to make it possible to remove a row from an interactive or regular SQL report. We’ll use jQuery and AJAX techniques to visually remove the row from the report and delete the record in the underlying database table. All this without the need for a page refresh. Have a look at the demo for the final result.
Let’s get started. The first thing you need is an interactive or SQL report. Make sure to include a primary key column in your source SQL query. The primary key will be passed as an argument to a JavaScript function called deleteEmp. After you created the report, edit the primary key report column and navigate to the Column Link section.
- Link Text: I refer to a workspace image garbage.gif.
- Link Attributes: Assign the link an onclick event that executes the function deleteEmp with as arguments this and the primary key column name substitution string.
- URL: javascript:void(0); will prevent a page refresh.
The next step is to create an on demand application process that deletes a record from the well-known table EMP. Notice the global variable which will contain the primary key on the moment of execution. I named the application process delete_employee and entered the following PL/SQL anonymous block:
begin delete from emp where empno = apex_application.g_x01; commit; end;
The last step exists of creating the JavaScript function deleteEmp which will be executed once the primary key link column is clicked. Edit the page attributes and paste the code snippet below in the Function and Global Variable Declaration under the JavaScript section.
function deleteEmp(p_this, p_empno) { // get the table row on which the user clicked var tr = $(p_this).closest('tr'); // perform an asynchronous HTTP AJAX request using jQuery $.ajax({ type: 'POST', url: 'wwv_flow.show', data: { p_flow_id: $('#pFlowId').val(), p_flow_step_id: $('#pFlowStepId').val(), p_instance: $('#pInstance').val(), x01: p_empno, // assign p_empno to the g_x01 global variable p_request: 'APPLICATION_PROCESS=delete_employee' // reference the application process }, beforeSend: // executes while the AJAX call is being processed function() { // delete following HTML classes from the table row element // could be possibly theme dependent tr.removeClass('even'); tr.removeClass('odd'); // use jQuery's animate function to give the table row, and its children, a red background tr.children().hover(function() { tr.children().animate({'backgroundColor': '#fbc7c7'}, 300); }, function() { tr.children().animate({'backgroundColor': '#fbc7c7'}, 300); }); tr.children().animate({'backgroundColor': '#fbc7c7'}, 300); }, success: // to be called if the request succeeds function() { // jQuery has difficulties animating inline elements // that's why we wrap them in a div, which is a block element tr.children().wrapInner('<div>').children().fadeOut(400, function() { tr.remove(); // visually remove the row from the report }); } }); }
That’s it. You can now delete an employee record by clicking the link in the primary key report column. The row gets a red background while the application process delete_employee is being executed. After successful execution, the row fades out and gets removed from the report. Don’t forget to check out the demo.
Hey ,
It is very useful post thanks.
I need some addition in this where my primary key is a combination of 2 columns value like EMPNO and DEPTNO. So how can I parse two values run time to delete the record on based of those 2 values ?
Please help.
LikeLike
Hey Jack,
You’ll have to add a second parameter at several places:
– the primary key report column link: onclick=”deleteEmp(this, #EMPNO#, #DEPTNO#)”
#DEPTNO# references the DEPTNO column in your report.
– modify the on-demand application process to take into account the DEPTNO value. Use apex_application.g_x02 to reference the second parameter.
– edit the JavaScript function parameters: function deleteEmp(p_this, p_empno, p_deptno)
– pass a second parameter to the jQuery AJAX request: x02: p_deptno,
That should do it. Good luck :)
LikeLike
Hey Nick,
Thanks for the quick response.
I actually tried the same way but some how it did not worked.Below is the code –
Link attribute – onclick=”deleteemp(this,#p_empno#,#p_deptno#)”
Java Script code –
function deleteemp(p_this, p_empno, p_deptno) {
// get the table row on which the user clicked
var tr = $(p_this).closest(‘tr’);
// perform an asynchronous HTTP AJAX request using jQuery
$.ajax({
type: “POST”,
url: “wwv_flow.show”,
data: {
p_flow_id: $(‘#pFlowId’).val(),
p_flow_step_id: $(‘#pFlowStepId’).val(),
p_instance: $(‘#pInstance’).val(),
x01: p_empno, // assign p_empno to the g_x01 global variable
x02: p_deptno,
p_request: “APPLICATION_PROCESS=delete_emp” // refer to the application process
},
beforeSend: // executes while the AJAX call is being processed
function() {
// delete following HTML classes from the table row element
// could be possibly theme dependent
tr.removeClass(‘even’);
tr.removeClass(‘odd’);
// use jQuery’s animate function to give the table row, and its children, a red background
tr.children().hover(function() {
tr.children().animate({‘backgroundColor’: ‘#fbc7c7’}, 300);
}, function() {
tr.children().animate({‘backgroundColor’: ‘#fbc7c7’}, 300);
});
tr.children().animate({‘backgroundColor’: ‘#fbc7c7’}, 300);
},
success: // to be called if the request succeeds
function() {
// jQuery has difficulties animating inline elements
// that’s why we wrap them in a div, which is a block element
tr.children().wrapInner(”).children().fadeOut(400, function() {
tr.remove(); // visually remove the row from the report
});
}
});
}
On demand run process code – delete_emp
BEGIN
DELETE
FROM emp
WHERE empno = apex_application.g_x01 and deptno =apex_application.g_x02 ;
END;
It is throwing an error in console as unexpected token for g_x02.
My deptno is alphanumeric like UK10, USA10.. etc.
Please suggest.
Thanks,
Jack
LikeLike
Jack,
Oops, I thought that the column DEPTNO had a numeric datatype. Alphanumeric values should be enclosed in single quotes. Edit the JavaScript function call in the report column link:
onclick=”deleteemp(this,#p_empno#,’#p_deptno#’)”
LikeLike
I’m trying this, and it’s working quite nicely. But in apex.oracle.com my garbage icon is different (it looks like an jpeg icon). The link looks similar. Any clue?
LikeLike
Hey Bart,
I’ve uploaded the garbage icon (garbage.gif) as a workspace image in shared components, under the images section. Then I referenced this icon by using the following notation: #WORKSPACE_IMAGES#garbage.gif. That’s how you display the icon on your web page.
I hope this answers your question, because I don’t fully understand what you’re trying to do…
Nick
LikeLike
Nice Exercise. Very Creative for Developer.
Thanks.
LikeLike
Nick,
Nice, but I think you’re making your life harder than you need to–APEX’s dynamic actions can already do a lot of the stuff you’re doing.
If you install the Sample Dynamic Actions packaged app (in APEX 4.2.2), go to the Server Side tab and look at the Refresh 2 example, you’ll see a similar implementation (though we included a confirmation & notification as well).
Regards,
-David
LikeLike
Some interesting examples there David. It’s true that using dynamic actions makes it a lot easier, but I personally think my example scores a bit better on user experience (apart from the lack of confirmation and notification messages). The difference is of course very small.
Thanks for the share.
Kind regards,
Nick
LikeLike
Hi Nick,
Very interesting post.
Thanks for sharing…
LikeLike
Hi Nick
Nice post. This fits nicely into what i was looking for. I could implement it first time with no issues.
thanks for your effort.
Karun
LikeLike
Hi Nick,
works nice.
One question: How can i update the row counter on the page, because deleting one row means one row less.
thanks
hg
LikeLike
Hello,
That’s a bit of a tricky thing to do. The easiest way is probably to refresh the report client-side. You could do something like this in JavaScript:
I don’t think there’s a more elegant solution. It is technically possible to decrease the pagination number with one in JavaScript code, but then things just get ugly.
Hope that helps,
Nick
LikeLike
sir i did the same thing when i click on delete link my row is delete and vanished but when i refresh the page it will come again ,can u tell me where i m mistaking becuz everything is working fine but record is not deleting so plz reply
LikeLike
It’s hard for me to say what the problem is. Everything is explained in the article. Do you get any console errors in your browser? I can take a look at your problem if you reproduce it on apex.oracle.com.
LikeLike
Hi Nick,
This is a great tutorial! I do have a question– how do you implement this using a classic report?
I did this on an IR and it works perfect. However, when done on a classic report, I get the delete animation but the record does not get deleted in the backend (thus, when teh page is refreshed, it’s still there).
Thanks in advance,
LikeLike
Hello Noel,
You normally should be able to apply this technique to a classic report. If you say the row gets visually deleted in the front-end, then I believe something is missing in your code. It’s hard to say what is missing of course. Do you get any browser warnings/errors? What is the response you get back from the AJAX call?
Can you reproduce the problem on apex.oracle.com? That way I can have a look at your code.
Best regards,
Nick
LikeLike
Hello Nick,
Thank you for the quick response, and my apologies for the delayed followup. I will put together a sample app soonest I can. (I’ve succumbed to a messy workaround of 1) adding a line in the function to set the pk to a hidden page item; that 2) fires a DA on change that does the delete in the actual table via PLSQL).
Best,
Noel
LikeLike
Hi Nick,
I just wanted to express my biggest of thanks to you and this gem of a code you posted!
I was finally able to resolve the problem with the code (my typo error, ugh!), but took it a step further — I made your code into a generic function placed within my external JS file. It’s also now tied to a custom jquery confirm dialog (with custom animation, css, etc.), and with the p_request “variable-ized” so that I can dynamically designate a process to be called by your inline delete function, based on the ‘onclick’ values of tables using that function– anywhere in my app (without having to modify the inline delete function itself). Just add another level of protection to the ajax call and it’s prod-ready!
Big big thanks again!
LikeLiked by 1 person
Hey Noel, sounds like you made some serious improvements to the code in this blog post. I’m glad you found it helpful.
Nick
LikeLike
Hi Nick, I really love your blog, I’m starting to implement some of your beautiful code.
One quick question, If I already have the link column used for editing the row….(linking to another apex page).
How can I have 2 links? (one for edit, one for deletion)?
Thanks!
LikeLike
Hello,
A column in a report can have a link defined as well. Navigate to the column properties page and fill in the settings under the “Column Link” section as described in the above article.
Best regards,
Nick
LikeLike
Hey Nick. Everything works functionally, but the color are showing up when the row is deleted on my app. Any idea what would prevent the colors from appearing?
LikeLike
Hello Steven. Remove lines 24 through 29 from the deleteEmp JavaScript function. The animate function puts a red background on the line that is being deleted.
LikeLike
Hello Nick. This is very useful post by you. Thanks for that :). I have a query though. How to show a popup to confirm the deletion every time the icon to delete record is clicked, again avoiding page submit.
LikeLike
Hey Prasad,
Use the confirm method: http://www.w3schools.com/jsref/met_win_confirm.asp
LikeLiked by 1 person
Excellent post Nick. Was very helpful as part of our zero-submit Shopping cart app.
LikeLike
Hi Nick,
Your post is wonderful and saved my ass ;).. However I have a question.
I created a table which contains attachment as BLOB content and the primary key is attachment_id and also there is a foreign key which is task_id and I have to delete the data from the following tables also
1. fnd_documents
2. fnd_attached_documents
3. fnd_lobs
all of which can be done by calling the following api fnd_attached_documents2_pkg.delete_attachments; Now out of the parameters passed 2 of them are task_id and attachment_id from the table.
The value for task_id is coming from an Item. but how can I pass the attachment_id value as a parameter to this procedure. Below is my Javascript function which i used (for now it is being used only to delete from the table only).
function deleteAttachment(p_this,p_attachment_id) {
// get the table row on which the user clicked
var tr = $(p_this).closest(‘tr’);
// perform an asynchronous HTTP AJAX request using jQuery
$.ajax({
type: “POST”,
url: “wwv_flow.show”,
data: {
p_flow_id: $(‘#pFlowId’).val(),
p_flow_step_id: $(‘#pFlowStepId’).val(),
p_instance: $(‘#pInstance’).val(),
x01: p_attachment_id, // assign p_empno to the g_x01 global variable
p_request: “APPLICATION_PROCESS=DELETE_ATTACHMENT_ID” // refer to the application process
},
beforeSend: // executes while the AJAX call is being processed
function() {
// delete following HTML classes from the table row element
// could be possibly theme dependent
tr.removeClass(‘even’);
tr.removeClass(‘odd’);
// use jQuery’s animate function to give the table row, and its children, a red background
tr.children().hover(function() {
tr.children().animate({‘backgroundColor’: ‘#fbc7c7’}, 300);
}, function() {
tr.children().animate({‘backgroundColor’: ‘#fbc7c7’}, 300);
});
tr.children().animate({‘backgroundColor’: ‘#fbc7c7’}, 300);
},
success: // to be called if the request succeeds
function() {
// jQuery has difficulties animating inline elements
// that’s why we wrap them in a div, which is a block element
tr.children().wrapInner(”).children().fadeOut(400, function() {
tr.remove(); // visually remove the row from the report
});
}
});
}
Thanks and Regards
LikeLike
Hey Anuj,
Edit the JavaScript function to accept another parameter. Make sure to pass a value for that new parameter. Then use x02 to submit a second parameter as part of the AJAX call:
Nick
LikeLike
Thanks Nick,
Will certainly try that
LikeLike
Hi. This works great. One quick question though. How to edit a single column of an interactive report and once it is edited, how to update the database with the newly entered string.
LikeLike
That’s not that easy. Why don’t you use a tabular form component for this requirement? Anyway, there are a couple of articles on the net that might help you out:
http://jaris.blogsite.org/apex/f?p=BLOG:READ:0::::ARTICLE:201402122308060084
http://jaris.blogsite.org/apex/f?p=BLOG:READ:0::::ARTICLE:137800346674748
http://www.talkapex.com/2009/09/updateable-interactive-report-for-apex.html
Kind regards,
Nick
LikeLike
Hai
LikeLike
Hi Nick: Thanks for this example, it gave me a starting point. In my case, I need to pass the record ID value, not the row to a function that will do an insert based on that ID and will call in a popup an Oracle form (I have the code for the stored procedure that will do this), however I don’t know how to retrieve the value of that particular row id. I don’t need the animation part. For now I am displaying the value of the parameter which is my record ID column from an Interactive report into an alert box in javascript onclick event.
LikeLike
Hey Alice,
It might be easier for you to work with a hidden item and dynamic action.
1. Create a hidden item on your page. This item will hold the ID of the row that has been clicked.
2. Include the ID column in your report. Make it a hidden column if you don’t want to display the row IDs.
3. Add another column to your report. The value for this column doesn’t matter. The type for this column should be Link. The target of the link should be something like this: javascript:$s(“P10_MY_HIDDEN_ITEM”, “#MY_ID_COLUMN#”);void(0);
4. Create a dynamic action that triggers on the change event of item P10_MY_HIDDEN_ITEM.
5. Using a true action of type Execute PL/SQL Code, you can perform the insert. You’ll have to fill in the Page Items to Submit attribute to set the hidden item’s value in session state.
Hope that helps,
Nick
LikeLike
Thanks so much Nick. I really appreciate your input. Actually I started working on this approach last Friday, based on another post. I’ll keep you posted with the end result. Unfortunately too busy at my work, other more urgent priorities take over this enhancement.
LikeLike
Hi Nick: Here is what I did step by step:
1. I created a link in my IR with the column that needs to pass value to the plsql stored procedure:
SELECT SPD_REC_ID, SPD_STUDENT_ID,
APEX_ITEM.HIDDEN(1,SPD_REC_ID) ||
APEX_ITEM.DATE_POPUP(2,NULL,spd_cns_ck_transcript_date,’MM/DD/YYYY’,10)
as “Checked For Transcripts”,
PRM_FIRST_NAME, PRM_LAST_NAME, SPD_DATE_CREATED,
–record ID is called below —-
CASE WHEN stu_reinst_petition.get_dq_status(spd_student_id) IN (‘A’,’R’) THEN
SPD_REC_ID
END “More Info “, — this is the link
—-
FROM
–rest of select statement follows.
2. The link
a. link text ->
b. target: URL -> javascript:$.event.trigger(‘DAEvent’,’#SPD_REC_ID#’);
c. The DA – Name -> Call DA from JavaScript URL
Details of the above: When condition – Event;- Custom; Custom Event-DAEvent; Selection Type – DOM Object; DOM Object – document; condition – No condition.
Execution Options: Fire When Event Result is – True; Settings: Set Type – Java Script expression – this.data.
3. P1_OTH_DQ_REQ holds a dynamic action of type PL/SQL which executes the stored procedure and generates as output a new sequence ID necessary to open the Oracle form. That sequence ID gets into another page item – P1_CNS_SEQ (for now I set it up as text item, with source used – ‘only when current value in session state is NULL’.
4. DA that calls the popup URL is attached to P1_CNS_SEQ as seen below:
Name of DA is ‘Call Go Page’ with action -> Execute Java Script code. The java script code opens the window as seen below:
popupURL(‘&FDQ_HOST./forms/frmservlet?form=&FDQ_FORM_NM.&otherparams=&FDQ_FORM_PARAM.=&P1_CNS_SEQ.&config=jre_sso’);void(0);
New problem: the value of P1_CNS_SEQ does not refresh and it keeps popping up the same record.
Note: is just to outline the part of javascript that goes into the action field of the DA.
Any idea of what I can do to refresh the content with each new click on the ‘Go Page’ link would be much appreciated.
Thanks so much,
Alice
LikeLike
The &P1_CNS_SEQ. substitution syntax is the problem I believe. That notation will always give you back the initial value of P1_CNS_SEQ from page load. That’s why you’re constantly seeing the same record. Try the following JavaScript code instead.
The $v(‘P1_CNS_SEQ’) function will return the client-side item value of P1_CNS_SEQ.
Nick
LikeLike
Thank you Nick. You got this perfectly right. I was on vacation and before I left, I struggled a bit through trial and error and came out with the the same idea (see below)
var seq = $v('P1_CNS_SEQ'); //was too scared to call this directly, LOL.
popupURL('&FDQ_HOST./forms/frmservlet?form=&FDQ_FORM_NM.&otherparams=&FDQ_FORM_PARAM.='+seq+'&config=jre_sso');
I love APEX but I go through a lot of frustration when it comes mix Java script with server side values of variables, quite a pain for me. But I am still learning. Will come and visit soon with other topics.
LikeLiked by 1 person
Nick,
This is great! I implemented and very slick, the rows delete… wow!
Keep up the good work! Posts like this are awesome.
thanks
Gaspar
LikeLiked by 1 person
This was actually my very first blog post. I’m glad it’s still being used :)
Thanks for the kind words,
Nick
LikeLike
Hi Nick,
I found this post quite useful. Could you also tell me how to add a row to an editable interactive report?
Sonika.
LikeLike
I’m not a big fan of editable reports to be honest. Anyway, the below JavaScript code gives you the ability to add an empty row to an IR. It performs the following actions:
– Create the lastRow variable that holds a jQuery object of the last row (tr element) in the report.
– Clone that object and append it to the parent of the lastRow variable.
– Clear all input items in the added row by using the :input jQuery selector
Keep in mind that the report should always contain a row in order for this approach to work. You’ll also have to change the jQuery selector of the lastRow variable. #employeesRpt for example is the static ID of the IR region. The a-IRR-table class does only exist in APEX 5.0. Older versions of APEX use different class names.
LikeLike
Hi Nick,
Can this solution be applied to tabular form?
Regards,
Troy.
LikeLike
Hello Troy,
That should be possible, but you’ll have to change the way the delete icon gets generated. Just add a column to the query that uses HTML to show the delete icon or text. The HTML should look like this:
You should then be able to follow the rest of the article without any problems.
Nick
LikeLike
Thanks a lot for the clarification, Nick. Really appreciated it.
I will give it a try.
Regards,
Troy.
LikeLiked by 1 person
Hi Nick,
I tried to use the statement above. It can generate the icon correctly.
I tried to use that in SQL statement as below. But when I clicked the icon, it didn’t do anything.
Please help to show where I might have missed.
select
'
' DEL_LINE,
"EMPNO",
"EMPNO" EMPNO_DISPLAY,
"ENAME",
"JOB",
"MGR"
from "#OWNER#"."EMP"
Thank you and regards,
Troy.
LikeLike
Hi Nick,
Sorry the codes didn’t display correctly.
It should be like below.
Regards,
Troy.
LikeLike
Hey Troy,
The EMPNO parameter in the deleteEmp function should be concatenated in your query. Try the following instead:
LikeLike
Hi Nick,
Great ! It’s working as expected now.
However, for new row / line, the icon is displayed as (null).
Is it supposed to be like that?
Could you please help to show how to solve this issue?
Thank you and regards,
Troy.
LikeLike
You’ll need some JavaScript code to replace the (null) value with the delete icon.
1. Assign a Static ID to the Tabular Form region (e.g. empTabForm).
2. Create the following JavaScript function and add it “Function and Global Variable Declaration”:
Make sure the “lastRowDeleteColumn” variable targets the delete icon column of the last row in the tabular form. You might have to change the jQuery selector depending on your HTML markup.
3. Change the Target URL of the Add button to:
javascript:apex.widget.tabular.addRow();afterAddRow();return false;
Here’s a link to a working example page: https://apex.oracle.com/pls/apex/f?p=58006:170
LikeLike
Hi Nick,
This is awesome. I tried with what you described. It worked perfectly.
Thanks a lot for your all help on this.
I really appreciated it.
Once again thank you.
Regards,
Troy.
LikeLiked by 1 person
Hi Nick,
I tried to apply your solution in classic report.
It did work perfectly.
And I added the Total sum for one column.
But I have a situation the Total didn’t get refreshed when I deleted the line.
Do you know how to resolve this issue?
Thanks so much,
Troy.
LikeLike
Hey Troy,
That’s actually the expected behavior. The totals in a report are being calculated on page load and after PPR (partial page refresh) of the report. The thing is that we don’t need a PPR in our situation since we remove the line from the report ourselves.
I think you have two possible solutions:
1. Instead of physically removing rows from the report with JavaScript, perform a standard report refresh to visually remove the deleted row. The report refresh will automatically re-calculate the totals.
2. Calculate the totals yourself after removing a row. You should be able to do this by writing some jQuery code that takes the sum of all values in a column. You can then replace the old total with your own calculated total.
I’d choose option 1 since it’s a more standard approach and easier to implement.
Hope that helps,
Nick
LikeLike
Hi Nick,
Thanks a lot for clarification and also direction with the options.
Really appreciated that.
Best regards,
Troy.
LikeLike
Wow thank you! Just what I needed and even I, a newbie to Apex and JS, did it! Very good explained.
LikeLiked by 1 person
Thank you! Why I don’t find this earlier?
The first article where I found clearly described all of the key components:
– Link text
– Application level process
– The JavaScript method executing the AJAX
– WHERE to put the JavaScript in the Page… (!)
I wish I had read through the comments earlier for the confirm() method… ;)
Some additional notes:
– x01, x02, … -> g_x01, g_x02, … are BUILT IN global variables, one should not try to declare them…
– Nowadays we can use the apex.server.process() from the APEX JavaScript API, instead of $.ajax(…), something like:
apex.server.process (“delete_employee”,
{x01: p_empno},
{dataType:”text”,
beforeSend: function() { … },
success: function() { … }
});
Again, Thanks a Lot! :D
LikeLiked by 1 person
Hello Nick.
I try your solution for row delete in report
After click on delete Icon I am redirected to a new page http://ip_address:port/ords/javascript(0);
with “404 not found” and the message: The request could not be assigned to any database.
After this window is closed and Applikaiton is restarted, I see that the delete process was successful!
Question:
Why am I redirected to a window with “404 not found” after delete?
best regards
Marek
LikeLike
Hello,
I found my error
was: javascriptvoid(0);
should be: javascript:void(0);
Thank you and regard
Marek
LikeLike