Sunday, March 18, 2012

af:serverListener and af:clientListener usage. Prevent User input to an input text component while an operation of another component is in progress. No glasspane, no splash screens. ADF 11g

Hi,

In this example we are going to create a small application that will demonstrate the following scenario:

We are going to have a radio Group and an InputText.


We are going to prevent the user from entering any value to inputText while the operation from the radioGroup is ongoing.

Download Sample Application.

In order to keep things simple and demonstrate the usage regardless the services behind, we are not going to have any  Business Components in this example.

Requirement:
We want to prevent users from editing the input text field while the operation of the radioGroup is ongoing.


Basically is not only that. We want to show to the user that there is an operation going on. But we do not want to show any popup ups or grey out the whole screen.

why?
Simply because our operation might take less time than the time needed to show a popup in our screen.
For example, it might take 1 to 1,5 seconds. So, if we were to use a popup, The second it would be shown, it will have to hide itself again..

Not very convenient is it? I know..

So for that reason, we are going to think it over a bit..


NOTE: in this post, we are going to follow another approach of explaining things...
We are going to follow the first thing that it might come in mind. Then we are going to think it over and see the simplicity of things. This is an experimental way of approaching a matter and covers the case where things are not that clear in our mind regarding the best approach to follow. Sometimes I find people accepting solutions "as is" without considering to challenge them. With this approach, we are going to rethink some approaches in order to determine which is the simplest and best. Make sure that you read until the end. We are all busy and we want the solution fast. But, trust me, there is more into it than just accepting the solution.


What can we do out of the box:


Well, we can have a af:clientListener to the radioGroup, in order to invoke  evt.preventUserInput(); through a javascript method.










Ok, so far, our main issue is resolved.. the user is not able to change any data to our inputText component..

But the user, is not a software engineer (at least in this fictional scenario..) and thus, she/he is not able to understand the phrase:


"you are not able to insert any data or change for that second, because you have changed the radioGroup choice  and this might take a few seconds to finish the operation. For that reason we block you from making any changes. Furthermore, if you had made any changes, those changes, will be lost because we are refreshing the input Text for several reason that are not so important to you right now."

"oh.. ok.. "

(she/he is going to forget it if she/he will not work on it for a month.. Then we will have the same discussion again.)

In order to avoid this rather unpleasant discussion, we have to make some indication in the application that there is an operation going on and the user is not able to make any changes..

I know!! we have the status indicator! It is easy to implement, fast to implement, it does not get any better than this!! This is the one! It is out of the box! a simple drag and drop! and viola!!

Well, this is a good thought!... But how are we going to apply this?

We will drag and drop it next to the input text.. Is it so difficult to understand that? It is easy, I can show you if you have difficulties in understanding the meaning of drag and drop...

Yeah sure.. lets arrange a meeting for that.. :)

What I am trying to say here is, that status indicator is good but does not indicate that the user is not able to make any changes to the components.

That is why a lot of implementations are applying the popup behavior, it is an indication that there is a process going on and the user is not able to make any changes to the application data nor able to press any other buttons.

I see, but if we are going to make the users understand that they cannot touch this ( have a break hit the link.. there are a lot of things going on in your head every day... I have 2 gold fish in an aquarium.. they do not remember anything for more than a second.. you cant blame them.. how many things can go through their heads..! :)   hammer time! watch the video.. )




Ok, so, what should we do?

.... (thinking)...
....(thinking)...
....(taking a zip of morning's coffee and rethinking)...
...( I need to throw away that coffee)...
...(thinking)...

I know!!
We will disable the input component!! (Do not hold your breath just yet... we are still thinking here)

Yee! Now we are talking.. Lets have a new cup of coffee.. and start developing..

WAIT!

 take a light coffee.. it is getting late..

Yes. We shall prevail!!

Disable the component. ok.  I still have some questions though..


  • How are we going to disable it?
  • When are going to disable it?
  • For How long are we going to disable it??
  • Is there is a way to re-enable it??
One thing at a time..

How are we going to disable it?

Can we do it in the javascript function? something like component.setDisabled(true) or something??
Well, no.
Why not?
Well, those properties are secured and are specially treated. Please see the relevant post of Frank Nimphius here

In short (please read the article above), we cannot just disable our input component, unless we will unsecure it. We do not want to unsecure it. We will try to find another way. Remember, we just want to make sure that the user understands that she/he cannot change anything while the process is ongoing.

oh.. I see. So, what can we do?  Cant we just cancel the event and proceed with the disabling the component. Yes we could. But this would also cancel the new value setting.

What we want is to, disable the input text, proceed with everything the radioGroup has to do and when this everying is done.. we want to enable back the input text.


OK, we could do the following: We could cancel the event, invoke the serverListener and do the setting there! Then we will enable it! All in one method!! (Do not just jump around now... We are still thinking here...).  So basically, we are going to do all in one method right? Right! But how are going to do that? We want to refresh the UI somewhere in between....

Oh.. Actually yes.. we want to:


  • Disable
  • process the process
  • Enable
Hmm... If we are going to do everything in one java method, how are we going to have this interaction with the UI?

Maybe we could invoke some javascript from the java method and then maybe we could invoke something else there.... (think of the maintenance here...)


It is already complicated as it is..  Dont you think?

Perhaps it is time to start rethinking here..



Rethinking

Let us see what we have here: We need to understand what to do and when.

First of all, we have realized that disabling the input text is not that easy as our initial thought... Especially with our case where we want to jump around, disable, process, enable and generally do funky stuff in our UI..

So perhaps we could take things slow..

Lets see our approach:

We are going to use an af:clientListener with a valueChange type in our RadioGroup. We are going to do this because we want to use a javascript method and "do something with the inputText". This javascript method is additionally going to prevent the user from any input as long as the operation in in progress. Then, we are going to use an af:serverListener and we are going to "undo that something that we did to the inputText" The  serverListener is going to be invoked via the  AdfCustomEvent.queue method. This method is going to be called from the clientListener java function.

Regarding the "do something with the inputText" We could start as a first implementation to hide it and after the process to make it visible.


Lets see what we can do with the component:
  • We can find it by id
  • We could hide it immediately.  Simply by calling component.setVisible(true) in our javascirpt method.
  • We could call a java method in our bean in order to show it again. Why should we make this last part? Simply because we will have to wait for the whole process to finish and go again into the Render_Response phase where the component will be refreshed through the partial trigger and it will be visible again. For those that are going to argue 

Simple is not it?

Lets see the amount of code we are going to write for this first implementation:

JavaScript:



Is not much right?

page Code:



As you will see the code is fair simple.


Java Code:


This is just the server Listener method.

At this point.. we are ready for our initial thought.

If you run this first implementation you will see that the input component is hidden while the process is there and becomes visible when it is done.

Why is this happening just like that?

Well, the clientListener is hiding the inputText. This is javascript and happens immediately.
Then, the clientListener is calling the serverListener method which in turn changes the visible property of the inputText.
This change is "visible" to the user after the refresh part that happens with the partial trigger.
It does not actually matters if the AdfCustomEvent  takes place first or last. After all, is a visibility change.

Yeah that is great! But what about the Disable part? We still want to disable the component as long as the operation lasts.

Well, given the fact that we cannot set the property from the javascript method, it adds up a complexity by itself. Furthermore, to unsecure the component is not a recommended approach. Thus, we will have to find other ways of making sure the user understands that the component is blocked for a time being.

Visibility is something that can work very well and with the minimum effort of development.
Now, if you want you can still "play" with this property and show/hide additional components.

For example, we could hide the inputText and show another component, lets say an output text that would have a small message. In the serverListener method we then just switch back the visibility properties.


To me, the visibility property is something that can be used in many ways and is rather easy to be applied.

Download Sample Application.

Regards.

References:
http://www.oracle.com/technetwork/developer-tools/jdev/1-2011-javascript-302460.pdf
https://blogs.oracle.com/jdevotnharvest/entry/using_af_serverlistener_as_a
http://docs.oracle.com/cd/E15523_01/apirefs.1111/e12046/oracle/adf/view/js/component/AdfUIComponent.html
http://docs.oracle.com/cd/E17904_01/apirefs.1111/e12419/tagdoc/af_serverListener.html
http://docs.oracle.com/cd/E12839_01/apirefs.1111/e12046/oracle/adf/view/js/component/rich/input/AdfRichInputText.html#getReadOnly__


No comments:

Post a Comment

LinkWithin

Related Posts Plugin for WordPress, Blogger...