The following solution will work with IRs and standard reports with pagination. If the users applies filters or paginates to another set of data, the changes they make will remain in the collection. This is using a similar technique that I wrote about for APEX Report with checkboxes (advanced). To summarize this process:
- Store the current query data into a collection as well as the md5 checksum.
- Build an IR against the collection and use APEX_ITEMs to display input fields.
- When a user changes a field, we submit that change to the collection.
- Once the user is done with their changes you'll need to process the collection as required. In the last step in this example I have a query that will help identify changed rows.
You can do a lot with this approach but if you don't have an urgent need I'd suggest holding off until APEX 4.0. They're some security issues that would need to be addressed before launching this code in a public application. I didn't include the security updates in this example since I did not want to lose scope of the base functionality. Updating the code to make it secure shouldn't be too difficult.
Here's the link to the demo: http://apex.oracle.com/pls/otn/f?p=20195:2300
- Create IR Report Region
Note: You can use this for regular reports with pagination as well
SELECT e.empno,
apex_item.text (2,
ac.c002,
NULL,
NULL,
'class="updateableIR" seqid="'
|| ac.seq_id
|| '" attrNum="2"'
) ename,
apex_item.text (3,
ac.c003,
NULL,
NULL,
'class="updateableIR" seqid="'
|| ac.seq_id
|| '" attrNum="3"'
) job,
apex_item.select_list_from_query
(4,
ac.c004,
'select E.ENAME d, E.EMPNO r from emp e where E.EMPNO != '
|| e.empno,
'class="updateableIR" seqid="' || ac.seq_id || '" attrNum="4"',
'YES',
NULL,
'- Manager -'
) mgr,
apex_item.text (5,
ac.c005,
NULL,
NULL,
'class="updateableIR" seqid="'
|| ac.seq_id
|| '" attrNum="5"'
) hiredate,
apex_item.text (6,
ac.c006,
NULL,
NULL,
'class="updateableIR" seqid="'
|| ac.seq_id
|| '" attrNum="6"'
) comm,
apex_item.select_list_from_query
(7,
ac.c007,
'SELECT d.dname d, d.deptno r FROM dept d',
'class="updateableIR" seqid="'
|| ac.seq_id
|| '" attrNum="7"',
'YES',
NULL,
'- Department -'
) deptno
FROM apex_collections ac, emp e
WHERE ac.collection_name = :p2300_collection_name AND ac.c001 = e.empno
- Create Region Items
- Hidden & Protected: P2300_COLLECTION_NAME
- Create Page Computation
Note: This is just to set the collection name. You can call it whatever you want
Item: P2300_COLLECTION_NAME
Computation Point: Before Header
Computation Type: Static Assignment
Computation: P2300_IR_COLLECTION
- Create a Page Process (PL/SQL)
Name: Load Collection
Process Point: On Lead - Before Header
-- This creates the collection if it isn't created yet.
DECLARE
v_collection_name apex_collections.collection_name%TYPE
:= :p2300_collection_name;
v_reset_flag VARCHAR2 (1) := 'N';
BEGIN
-- Create collection if it does not exist or reset collection required
IF apex_collection.collection_exists
(p_collection_name => v_collection_name) =
FALSE
OR v_reset_flag = 'Y'
THEN
apex_collection.create_collection_from_query
(p_collection_name => :p2300_collection_name,
p_query => q'! SELECT empno,
ename,
job,
mgr,
TO_DATE (hiredate, 'DD-MON-YYYY'),
comm,
deptno
FROM emp !',
p_generate_md5 => 'YES'
); -- Generated md5 is important to help identify changed columns
END IF;
END;
- Create HTML Region to store JS code:
Note: This uses jQuery and Tyler Muth's jApex plugin. You'll have to upload the .js files as static files in Shared Components.
- Create Application Process:
Name: AP_UPDATE_COLLECTION_CELL
Process Point: On Demand
-- AP_UPDATE_COLLECTION_CELL
BEGIN
apex_collection.update_member_attribute
(p_collection_name => apex_application.g_x01,
p_seq => apex_application.g_x02,
p_attr_number => apex_application.g_x03,
p_attr_value => apex_application.g_x04
);
END;
- Query to see which rows were changed
SELECT ac.collection_name, ac.seq_id, ac.c001, ac.c002, ac.c003, ac.c004,
ac.c005, ac.c006, ac.c007, ac.md5_original,
CASE
WHEN apex_collection.get_member_md5
(:p2300_collection_name,
ac.seq_id) =
ac.md5_original
THEN 'NO'
ELSE 'YES'
END row_change
FROM apex_collections ac
WHERE collection_name = :p2300_collection_name
Martin,
ReplyDeleteI like it! Good post.
Regards,
Dan
Very creative thinking. Thanks for sharing.
ReplyDeleteAshish
http://www.dbcon.com
Looks cool. But did you try to set highlighting? If you use the dropdown list for the expression you don't get what you want (and if you just enter something it doesn't work either). The same holds for selecting a filter via the column header. Sorting doesn't work, computations don't work, charts won't work....so it is not websheets yet....
ReplyDeleteCheers
Roel
Hi Roel,
ReplyDeleteYou're absolutely right. Filtering or applying any IR functionality against columns that have inputs isn't going to work. To help out, I've changed the demo app to remove functionality on those columns. I've also set the deptname as display only so that you can filter on it and still see your changes etc.
You're also right that this is not a websheets replacement. It does what it does and nothing more. Its main goal is to allow end users to filter on non-input columns then update the appropriate data. Anything more than that and this is not the right solution.
Thanks for your feedback.
Martin
Hi Martin,
ReplyDeleteReally interesting solution.
Just one suggestion for the next version - I think you don't have to copy all data to collection - you could use outer join in IR query - then collection will have only updated rows.
It's not websheets but this solution is very helpful.
Lev
Hi Lev,
ReplyDeleteUsing the outer join is an excellent idea for rows that can't be updated. You also don't need to add all the columns into the collection, just the updateable ones.
I wanted to leave things as simple as possible for this example, so please don't take it as a production ready solution.
Thanks for your feedback.
Martin
Martin,
ReplyDeleteI think that a viable workaround for the APEX_ITEM display issue (Roel - September 15, 2009 1:25 AM) is as follows:
- for each APEX_ITEM item, also include the 'raw' value from the collection - do not make this visible in the report. Educate users to only use this column for filtering etc and to not make it visible in the report.
- disable filter etc on the apex_item item, but not on the 'raw' value
- user can now filter on the 'raw' value, which will appear in the column list under the 'Other' option group.
Until APEX matures to the point that this functionality is available out of the box we have to do workarounds like this in order to meet our Business requirements.
Thanks,
Paul Duncan
Martin
ReplyDeleteI'm trying to use your code to build a Questionnaire.
I have build the collection, the IR report loaded the Jquery and jApex script into static files.
The problem is the changes aren't being reflected from screen to collection.
I'm not sure what
//Set all updateableIR columns onChange events
eludes to.
Do I need to setup onChange events for the text, radiogroups and select lists I have setup in the IR.
I'm a PL/SQL programmer with no javascript
Regards
Derek
Hi Derek,
ReplyDeleteI may have mislabeled my comment "//Set all updateableIR columns onChange events" really binds the onchange event to all your updateable columns.
So no, as long as you include that class on columns that have inputs, as in my example, everything should be taken care of you with the above code.
If you're still having issues, you could post you code on apex.oracle.com and I can take a look.
Martin
Hi Martin
ReplyDeleteI have put a stripped down version of my Questionnaire app to save time on to apex.oracle.com
I have put in the custom_auth used by oracles sample I hope that helps
URL http://apex.oracle.com/pls/apex/f?p=39536:1:127057230376098:::::
demo/winton
The code to update the collection is copied pretty much exactly, but doesn't update either, no surprise
If there's an access problem let me know what I need to do.
Extremely grateful for your reply
Thanks
Derek
Hi Derek,
ReplyDeleteI just tried and I think I can see the problem, however I'd need access to your workspace to modify the application.
Could you please create a developer user and send me the workspace login credentials to myfirstnameATclarifit.com?
Thank you,
Martin
Hi Martin
ReplyDeleteThe developer signon should be with you now.
Thanks
Derek
Thanks Martin
ReplyDeleteFor your help in getting my Updateable IR working with your code.
This is a really great piece of software I'm sure many besides myself are finding it immensely useful.
Thanks
Derek
hi Martin,
ReplyDeleteI created the variosu regions, collections & then uploaded the JS queries etc bu the updateable columns are not updatng or saving the changes done. how do i resolve this now? it seems ot be same problem like Derek had is what I'm facing now? (ie Changes are not saving)
I checked that the jquery file I put out in the static file was a new version (jquery-1.4.min.js). I changed the javascript to indicate this new version as
ReplyDelete//SCRIPT src="#APP_IMAGES#jquery-1.4.min.js" type=text/javascript></SCRIPT// . However this is still not working as it should? Any insights is assisted
Hi Ananth,
ReplyDeleteDo you develop in Firefox and use Firebug? Can you look at the console to see if anything is happening when you change a value in the IR?
Thank you,
Martin
Thanks martin for your questions. I'm using Internet Explorer 7.
ReplyDeleteI will chec kthe console and then post my comment again for that.
Hi Martin,
ReplyDeleteyour example is also useful several years later.
Unfortunately I have a problem with the update. I can not see any reaction also using a button to refresh the result query. Maybe it is possible to give a possibility to download your page for this example ?
Many thanks,
Birthe
Hi Birthe,
DeleteI'm happy to hear that it's still useful to people several years later. I won't be publishing a downloadable demo as it's an old post (and all the code is above).
Here are some tips to help debug:
- Put some logging information in your Application Process which updates the collection. This will let you know if you're actually updating the collection.
- Use firebug to make sure the AJAX request is going through.
- Create another report region that is a SELECT * FROM APEX_COLLECTIONS to see what you currently have in your collections. You can create a button/dynamic action to refresh this report to see if your collection is being updated.
Hope this helps,
Martin
Hi Martin,
ReplyDeleteI was trying your example in apex 4.2 but it throws an error while creating on demand process for "AP_UPDATE_COLLECTION_CELL" as
error : " To create an on-demand page process, at least one application level process must be created with the type 'ON-DEMAND'". Please tell me how to overcome that and also you have mentioned about uploading .js files please tell me where i will get that. I
have pasted your Java script given on this page to Html Header so is it correct.
Regards,
Rajat
Please read the section called "Create Application Process" in this post.
DeleteHello Martin,
ReplyDeleteFirst of all thank so much.
I am facing Issue in "downloading editable Interactive Report"
I am using APEX version -> Application Express 4.0.2.00.07 ,Database 11g.
One of my field in the Interactive Report (Dname ) is editable means user can update the value of DNAME but when I am downloading my I.R (csv format ) using Action button ,the DNAME is coming in the below format,I want to be in the same format as EMPNO.
EMPNO DNAME
1234 <input type="hidden" ..........so on
Note: I am using I.R query as
SELECT
"EMPNO",
"APEX_ITEM.HIDDEN(1,EMPNO)|| APEX_ITEM.TEXT(2,DNAME) "DNAME"
FROM EMP.
Please Help.
Thank you..
Regards,
Gaurav
Hi Gaurav,
DeleteThis article should resolve your issue: http://www.talkapex.com/2010/06/how-to-only-display-column-when.html
Martin
Hi. How can I add new row to updateable interactive report?
ReplyDeleteThank you Martin :-)
ReplyDeleteHi Martin,
ReplyDeleteCan you share the application for Apex 5.0 version. I am not able to get the jquery and jApex plugins.
Jaya, as this is an old post I won't be updating it for APEX 5.0. Also APEX 5.1 has the Interactive Grid which is much better than this solution.
Delete