Saturday, September 13, 2008

JDeveloper 11g - CRUD in ADF Form Component

I'm again back to one of most popular topics on my blog - CRUD implementation in ADF. I was blogging previously about CRUD in 10g and also about CRUD in 11g af:table component - JDeveloper 11g - Create, Edit and Delete operations in ADF Faces af:table component. Today I want to describe CRUD in ADF Form implementation, it is different comparing to ADF Table implementation. Actually, its easy to implement CRUD operations in ADF Form, however it can be complex to implement user friendly CRUD behavior. Main focus of my blog post will be to describe how to achieve user friendly CRUD behavior with ADF Form component.


This post is based on sample application I have developed - UndoCreate.zip. Sample is based on standard HR schema, before running application make sure you have correct DB connection defined.

1) Find Functionality

By default, when you are using ADF Search Form functionality, entered query parameters are stored and are shown again when user will re-open form in Find mode. However, based on my experience, usually it is not a desired behavior. Users prefer not to see old query parameters when they re-open form in Find mode. For example, when some query parameter is entered:


Search is executed and result returned:


And now, when user will press Find button again, previous query criteria should be cleared. It is not cleared by default in ADF, however it is cleared in my sample application:


I'm clearing previous query parameters by executing combination of Rollback, Find, Delete and Create actions in backing bean findButton_action() method associated with Find button.

2) Find Functionality - No Results

When user enters query parameter, that will not give any result, and press Search button - empty form will be returned by default. Example of non-existing query criteria:


Empty form returned by default:


In my sample application, I have changed this default behavior in executeButton_action() method in backing bean. I'm doing a check - if iterator is empty, I'm returning to Find mode and displaying information message - 'No Results found!':


In this case I'm not clearing entered query criteria.

3) Undo Functionality in Create Mode

When form is in Create mode, user usually prefer to have 3 choices - to store entered data and to go to Edit mode, to clear entered data and to remain in Create mode, and to close Create mode by opening form in Find mode:


If with 2 cases - to store entered data or to open form in Find mode everything is less or more okej, there is a problem with a case when user wants to clear entered data and to remain in Create mode. With default Undo functionality, when using only Rollback action, user will be navigated back to Edit mode. In order to change this, I'm re-invoking Create mode initialization from backing bean in undoButton_action() method, it is invoked after default Rollback action. This allows to clear entered data and to stay in Create mode after Undo button is pressed:


4) Undo Functionality in Edit Mode

By default, when Undo button is pressed while in Edit mode and Rollback action is executed, changes will be discarded and at the same time form will be refreshed and will show first row from iterator. This means, user will be navigate from a row he is currently editing to a first row in iterator.

To make this behavior more user friendly, I have implemented custom Rollback functionality in undoButton_action() method in backing bean:


In this code, I'm acquiring form related iterator, getting current row user is editing and refreshing it. At the end, I'm applying Partial refresh to a form in order to display correct data. More information about Rollback functionality you can find in Chris Muir blog post - JDeveloper and the art of the rollback. The same functionality can be applied not only in 11g, but in 10g as well.

With implemented functionality, if user is editing data:


And decides to remove all changes by pressing Undo button, form will remain on the same row:


5) Delete Functionality

When user executes Delete functionality, I can recommend to commit transaction at the same time. In developed sample application, when Delete button is pressed - deleteButton_action() method is invoked, where confirmation popup is opened:


When user confirms Delete action, Commit action is executed at the same time, and row is removed from database.

6) Delete Functionality - Last Row

By default, after user will delete last row from iterator, empty form will be rendered:


Such behavior is not user friendly, much better to navigate to Find mode automatically:


This logic is implemented in deleteDialogListener(DialogEvent dialogEvent) method in backing bean:


7) Confirm or Undo Pending Changes

When user is editing data and there are pending changes (before commit or rollback), based on my experience its better not allow to navigate from current row. For example, when data is changed and there are pending changes:


Buttons related to form logic - Find, Create, Delete and buttons related to form navigation - First, Previous, Next and Delete are blocked. Information message is shown - 'Save or Undo Changes First!' asking user to confirm or dismiss current pending changes:



Information about pending changes is retrieved from bindings.Commit.enabled using Expression Language. Its enough to edit data available in the form and bindings.Commit.enabled will return true.

30 comments:

IBK said...

nice :)

dfdf said...

Hi Andrejus,

I observe separate "create" activity for performing create action.

Can't we do it in the backing bean programatically?.

Andrejus Baranovskis said...

Hi,

It is possible to invoke Create programmatically from Backing bean.

But, I would recommend, if it is only possible, to do it declaratively.

Regards,
Andrejus

Anonymous said...

I noticed in both examples(10g and 11g) that in order for the tutorials to work, the Managed Bean must be scoped as session level and request level.

This can be very memory eating process. Image there are many pages in a web application.

Is there a way to do this thing using the request scope instead of session scope???

I worked around this by using the inline style and some javascript events to make text boxed readonly.

Moreover, if there is a boolean value in the view, can it be bound to a check box ????????

Sandhya said...

Hi Andrejus,

I am trying to implement this for find operation and every time I am getting "Attempt to re-register component with different model." For the find Any pointers why?

Sandhya

Prasad Challagundla said...

Hi Andrejus,

I have implemented the search functionality by following your guidelines in the blog and it is working fine if I don't have any bind variables for the query.

We have a requirement in which the form can have only one record in the search criteria.

we have to fetch data based on the search criteria entered by the user (which is fine).

We have to fetch the default data if the user does not enter the query criteria.

How to implement this solution for our scenario.

Thanks and Regards,

S R Prasad

Andrejus Baranovskis said...

Hi Prasad,

You may consider to use ADF Query Criteria component - it will simplify development: http://andrejusb.blogspot.com/2008/11/adf-query-component-and-view-criteria.html

Regards,
Andrejus

Naser said...

I have 2 ADF forms in an application

form A do CRDU on entity a and form
B do CRUD on enrity b
i need to call form B from form A
when i insert a record in form A
but not save it then go to form B and insert a record and save
record that i have inserted in page A
automaticaly commited.
have can i prevent this action?

James said...

Hi Andrejus

I have written a blog on ADF 11g Crud operation and also its source code is available for download , can i post the link on your blog ?

Regards

Andrejus Baranovskis said...

Sure, thanks for asking - post a link here.

Andrejus

James said...

Thanks Andrejus

This blog article covers how ADF 11g can be used for CRUD Operations.

http://blogs.oracle.com/fusionmiddlewarereallife/adf_11g_crud_operations_on_a_master_detail_table.html

My other ADF 11g related articles are at

http://jamessmith73.wordpress.com/

which covers basics of ADF like working with variables, task flows, creating portlets, publish and subscribe events etc

Regards
James [ Madhu ]

Stew said...

Hi Andrejus,

Is it possible to create a form to insert a new record using only one submit button?

Currently I add createInsert and Commit buttons from Data Controls' operations. I'd like to do that using just one button.

Thanks, Stew

Anonymous said...

Hi Andreus I have a similar functionality needed and downloaded your link but not sure how you did this. Any chance you could point me to a step by step tutorial or just guide me through it?

Anonymous said...

I double clicked the next button on my page and in the dialog created a backing bean like in your example. I copied all the imports from your backing bean to mine. I then created the JSFUtil class and copied yours into it and then imported it into the main backing bean just like yours. The problem I am having is I can't generate accesors in my main backing bean. I have to double click everything in my page in order for it to happen and even then I don't get the
private richform Form1 like you do.

Andrejus Baranovskis said...

Hi,

There is no need to create accessors for every component. You can go to component properties screen and find Binding property, it will be one of the last properties. Define binding name and it will generate accessor.

In Red Samurai, we are offering advanced ADF coaching, drop me an email, if you are interested.

Regards,
Andrejus

Anonymous said...

Hey Andreus I've been going over your project trying to recreate your functionality in my project but am having trouble. In your project the changes pending dialog opens up when I try to navigate away from the record which I changed but I can't see it anywhere in your web page. Is it automatically called from JSFUtils bean? If so I would guess you call it from your Main bean but what code is this? Could you point me to it? Thanks in advance

Anonymous said...

I think I actually found the code

public String prviButton_action() {
if(((Boolean)JSFUtils.getManagedBeanValue("bindings.Commit.enabled")) == false) {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Prvi");
Object result = operationBinding.execute();
if (!operationBinding.getErrors().isEmpty()) {
return null;
}
} else {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Save or Undo Changes First!", null));
}

return null;
}

But even when I copy it for my First button (just to test it out) it doesn't work. When I make changes and press the button nothing happens, there is no dialog that pops up. Even though I have the JSF Utils thing in my project (should it be in some particular folder in order to work?)

Anonymous said...

Ok I got somewhere but here is something that is definetly a valid question. In your example, when you make a change and then press undo, the pop up will still come up if you try to navigate using the buttons. As if you didn't undo the changes at all. Could you adress this, maybe tell me what to change so that when undo is done it doesn't pop up anyomore

Andrejus Baranovskis said...

Hello,

It should work fine with Rollback operation and resetActionLister mapped to Undo button.

Andrejus

Anonymous said...

Hello Andrejus.

The listener is mapped to the undo button and the action is the undo button method as defined in the main.java. From what I saw the action calls the rollback but the pop up still comes up. Does it work for you? I downloaded it from here, try doing that and then check if when you change something and then press undo, can you navigate without pressing save.

Andrejus Baranovskis said...

I believe it was working as you describe. But, I will check today with latest release. Will let you know.

Andrejus

Anonymous said...

Thanks Andrejus, I downloaded it from this page so if it works for you could you post me the version you have so that I can check it out, see if its my machine

Anonymous said...

HeY Andrejus what el expression would you use for the rendered property of navigation buttons, so that when the user clicks Create, they aren't rendered (but only in that context)

I seem to either render it all the time or not at all. Here is my code

rendered=#{!bindings.CreateInsert.execute}

Andrejus Baranovskis said...

Hi,

I have double checked this app on last JDev version. Yes, it have incorrect Undo behavior as you describe. You can fix it by adding this code (Commit after Rollback invocation):

BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("Commit");
operationBinding.execute();

You should add this code in undoButton_action function, first IF statement.

Regarding EL expressions..., you cant user *.execute inside Rendered property. Because *.execute invokes action.

Regards,
Andrejus

Anonymous said...

Andrejus when I press create and insert data for a new row. I press commit and the data commits. I then press commit again (as a test see) and I get an error message in firefox, but there is nothing in Jdeveloper, no error message. What could be causing this? The message is quite long so I won't be posting it at this time but if you want me to I will.

Anonymous said...

Hey Andrejus how did you hide your navigation buttons when you press create? Did you use rendered or some other property?

Andrejus Baranovskis said...

Hello,

You can contact me for Red Samurai advanced ADF training/coaching... We can help to speed up your project and make sure all on right track.

Let me know.

Regards,
Andrejus

Anonymous said...

Hi Andrejus, I'm faceing troubles when I'm using refresh Row.REFRESH_UNDO_CHANGES, The problem presents when I have a view object with a base entity object and other entity objects working as references ... the framework always raise a validation about need the required fields of the referenced entities, but I dont understand this behavior, because I create the entities as reference usage ... Could you help me?

theodore koffi said...

Hi All,
Can someone help me to implement createwithparameter operation in ADF ?

Andrejus Baranovskis said...

Yes, I have blog post about it: http://andrejusb.blogspot.com/2011/02/createwithparams-operation-for-oracle.html

Andrejus