Details
-
Type: New Feature
-
Status: Closed
-
Priority: Major
-
Resolution: Fixed
-
Affects Version/s: 1.8.2
-
Fix Version/s: 1.8.2-EE-GA_P01, 1.8.3
-
Component/s: ICE-Components
-
Labels:None
-
Environment:All
-
ICEsoft Forum Reference:
Description
Currently, ICEfaces intercepts all form submissions, to be able to do its own form field serialization, and bundle in ICEfaces specific submitted values. This keeps the browser's default behaviour from happening. When seeking to address user's desire of default browser behaviour, it's been noted that certain issues make it suboptimal. For example, in stock JSF applications, if there are multiple submit buttons, then there is not consistent behaviour across browsers, as to which submit button will be virtually clicked. Solving the simple case would likely break more complex cases. Furthermore, after DOM manipulation, which submit button is clicked becomes arbitrary in some browsers. There is also the question of forms with input fields, and no submit button, which is quite popular with search fields.
Historically, ICEfaces addressed these issues by allowing applications to specify an action/actionListener on certain input fields directly, so that if ENTER was pressed in that specific input field, a precisely specified action/actionListener could be invoked. This fine grained control becomes a detriment when there are many input fields in a form, or when using multiples levels of JSP or Facelets inclusion, and the application wishes to always invoke the same action/actionListener.
So, to complement existing behaviours, we propose adding action and actionListener attributes to ice:form, so that there may be a single place to specify the action/actionListener, regardless of the submitting component, whether it be an input field or UICommand subclass like commandButton. When any of those components cause a full form submission (not a partialSubmit), they will be given a chance to invoke their own action/actionListener, but if they have none specified, then the ice:form will invoke its own action/actionListener, if one was specified. In this way, a catch-all action/actionListener may be specified on the ice:form, which can be overridden on a per component basis.
-
Hide
- ICE-5122.war
- 6.42 MB
- Adnan Durrani
-
- META-INF/MANIFEST.MF 0.0 kB
- WEB-INF/classes/bean/Employee.class 1 kB
- index.jsp 0.1 kB
- WEB-INF/lib/xercesImpl.jar 1.15 MB
- WEB-INF/lib/icefaces-comps.jar 1.95 MB
- WEB-INF/classes/bean/Bean.class 6 kB
- WEB-INF/lib/commons-collections.jar 558 kB
- WEB-INF/lib/backport-util-concurrent.jar 319 kB
- WEB-INF/classes/bean/Bean.java 5 kB
- WEB-INF/lib/commons-beanutils.jar 226 kB
- WEB-INF/lib/commons-logging.jar 52 kB
- WEB-INF/lib/jstl.jar 20 kB
- main.jspx 7 kB
- WEB-INF/.../krysalis-jCharts-1.0.0-alpha-1.jar 151 kB
- WEB-INF/classes/bean/Employee.java 0.9 kB
- WEB-INF/web.xml 2 kB
- WEB-INF/lib/xml-apis.jar 190 kB
- WEB-INF/lib/icefaces.jar 1.19 MB
- WEB-INF/lib/commons-digester.jar 140 kB
- WEB-INF/lib/jsf-api.jar 356 kB
- WEB-INF/lib/jsf-impl.jar 778 kB
- WEB-INF/lib/commons-fileupload.jar 56 kB
- WEB-INF/faces-config.xml 0.5 kB
-
Hide
- ICE-5122-2.war
- 5.59 MB
- yip.ng
-
- META-INF/MANIFEST.MF 0.1 kB
- WEB-INF/classes/bean/Bean.class 6 kB
- WEB-INF/classes/bean/Bean.java 6 kB
- WEB-INF/classes/bean/Employee.class 1 kB
- WEB-INF/classes/bean/Employee.java 0.9 kB
- WEB-INF/faces-config.xml 1 kB
- WEB-INF/lib/FastInfoset.jar 285 kB
- WEB-INF/lib/backport-util-concurrent.jar 319 kB
- WEB-INF/lib/commons-beanutils.jar 226 kB
- WEB-INF/lib/commons-collections.jar 558 kB
- WEB-INF/lib/commons-digester.jar 140 kB
- WEB-INF/lib/commons-discovery.jar 75 kB
- WEB-INF/lib/commons-el.jar 110 kB
- WEB-INF/lib/commons-fileupload.jar 56 kB
- WEB-INF/lib/commons-logging.jar 52 kB
- WEB-INF/lib/icefaces-comps.jar 1.95 MB
- WEB-INF/lib/icefaces.jar 1.19 MB
- WEB-INF/lib/jsf-api-1.2.jar 352 kB
- WEB-INF/lib/jsf-impl-1.2.jar 822 kB
- WEB-INF/lib/jstl.jar 20 kB
- buttonAction.jspx 0.4 kB
- formAction.jspx 0.4 kB
- index.jsp 0.1 kB
- linkAction.jspx 0.4 kB
- main.jspx 7 kB
- textAction.jspx 0.4 kB
- WEB-INF/web.xml 2 kB
Activity
- All
- Comments
- History
- Activity
- Remote Attachments
- Subversion
Yip and I look both options and discussed about it.
The first option seems good, but it require changes in couple of components as well as it won't work with third party components.
The second option seems pretty simple, with minor changes but it won't work with UISeries or UIData.
So Yip and I trying to see if the second option can work with UISeries as well.
We have identified a way by which we need to make changes only in the FORM component, and the RowEvent has to be defined public in UISeries. it will work with third party components as well as ice:dataTable and ice:panelSeries.
public void queueEvent(FacesEvent event) {
FacesEvent tempEvent = event;
//if its a rowEvent, then get the actual faces event
if (event instanceof RowEvent)
//now check if its an action event
if (tempEvent instanceof ActionEvent) {
//see if the event source has actionListener defined on it
Object listener = tempEvent.getComponent().getAttributes().get("actionListener");
//if actionListener is not defined on the source component AND the form component defines one then it means that form should take care of it
if (listener == null && getActionListener() != null) {
now create a new ActionEvent, so the form component can be set as an event source
FacesEvent newEvent = new ActionEvent(this);
//if its a rowEvent, then swap the actual FacesEvent, this will give us a benefit of dealing with UISeries event, and form.broadcast will be called in a proper iteration
if (event instanceof RowEvent)
else
{ //this component is not inside any UIData, so just queue actionEvent blongs to form super.queueEvent(newEvent); return; } }
}
//it means that either the event was not an actionEvent or if it was an actionEvent but the component has defined an actionListener as well.
super.queueEvent(event);
}
//this should be called with only those actionEvents which are not handled by their components.
public void broadcast(FacesEvent event) throws AbortProcessingException {
}
Don't forget that if you're unwrapping events from their RowEvent wrappers, you have to do that recursively, since there could be arbitrary levels of UISeries inside that form, that are parent to the true event source.
As well, you have to search for for actionListener and action on the true source component, not just actionListener. re we sure that UICommand components (h: and ice will still queue ActionEvent, because they decoded the click as being them, even if they have no action or actionListener? I seem to recall little optimisations like that in our ice: components. We'll want to make them always queue.
In our code that unwraps UISeries.RowEvent, it would be nice if we could try to add a reflection approach to unwrapping UIData's wrapper event type too. We might have to have code for Sun RI and MyFaces.
>> "Don't forget that if you're unwrapping events from their RowEvent wrappers, you have to do that recursively, since there could be arbitrary levels of UISeries inside that form, that are parent to the true event source. "
The form doesn't need to perform any recursion, its already done by the UISeries. When form.queueEvent() will be called it will be already in a valid iteration, even if they are nested UISeries, however its a good test case to test nested UISeries.
>>"As well, you have to search for for actionListener and action on the true source component, not just actionListener."
Yes that is true that we should look for action and actionListener, but right now we want to make sure if this solution is reliable, so currently we are looking for actionListener only on the true source component. After its successful testing we are going to add action as well.
>>"are we sure that UICommand components (h: and ice will still queue ActionEvent, because they decoded the click as being them, even if they have no action or actionListener? I seem to recall little optimisations like that in our ice: components. We'll want to make them always queue."
Yes, UICommand component queues the event regardless of if they have action or actionListener defined, and this is what we want, so we can capture those queue events.
>>"In our code that unwraps UISeries.RowEvent, it would be nice if we could try to add a reflection approach to unwrapping UIData's wrapper event type too. We might have to have code for Sun RI and MyFaces."
Yes, that would be an enhancement to get this thing working with h: and t: dataTable as well.
Changes added.
Modified: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Modified: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\panelseries\UISeries.java
Sending content: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\panelseries\UISeries.java
Sending content: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Completed: At revision: 19865
Modified: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Sending content: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Completed: At revision: 19866
When there is a RowEvent wrapping an ActionEvent, is the "source" of the RowEvent the UISeries, or the originating UICommand? Because if it's the UISeries, then let's think of what happens with:
<ice:form>
<ice:dataTable>
<ice:panelSeries>
<ice:commandButton>
You get an event in HtmlForm.queueEvent that is a RowEvent for the dataTable. You unwrap that, and end up with a RowEvent for panelSeries. You then check if there's an actionListener on the RowEvent from the panelSeries. But you probably want to keep unwrapping until you get down to the actual ActionEvent from the commandButton, so when you check its "source" compoennt you're getting the commandButton, and your check for an actionListener is valid. That's why I believe you have to recurse down into the events, unwrapping them, instead of just trying to unwrap once.
Say we have an inputText that does not have an actionListener. Will it queue an ActionEvent when you press ENTER in it? Or will it check if it has an action or actionListener first? We should make sure it will queue the event regardless, so that the form will do the right thing.
I got what you mean, I thought you were talking about the recursion of DataModel. Yes, this has been already taking care of.
Demo shows simple components
http://screencast.com/t/OWNiZmNk
Demo shows components inside nested UIData/UISeries.
http://screencast.com/t/ODk2YjY0MjE
Modified: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Modified: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component-metadata\src\main\resources\conf\ice_properties\ice-form-props.xml
Sending content: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component-metadata\src\main\resources\conf\ice_properties\ice-form-props.xml
Sending content: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Completed: At revision: 19869
TODO: action
Just to make sure that the form should only take over on ActionEvent when the event target is not defining both action and actionListener? For example in this case, form should not be doing anything?
<ice:form action="#
{bean.action}" actionListener="#{bean.actionListener}"><ice:input value="Click" action="#{bean.action}
" />
Correct. I believe that once they specify any action or actionListener on the input, the developer is wishing to replace the form's default behaviour. Especially since developers tend to not use both action and actionListener, just one. Since they would most likely not want to blend action and actionListener at one level, it's all the more likely they wouldn't want to blend them at different levels.
Added checking for "action".
Revision: 19876
Modified : /icefaces/trunk/icefaces/component/src/com/icesoft/faces/component/ext/HtmlForm.java
Demo showing form and component actions:
http://screencast.com/t/MTc3YTQyZWY
war file for demo showing form and component actions: ICE-5122-2.war
More restricted, so the form should only take over on ActionEvent if the form component is defining either an "action" or an "actionListener".
Modified: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Sending content: D:\work\development\head\svn\ossrepo\icefaces\trunk\icefaces\component\src\com\icesoft\faces\component\ext\HtmlForm.java
Completed: At revision: 19881
The following two attributes are added to the form component:
- action
- actionListener
To fix this issue, the changes has been made so the form component can takeover on ActionEvent if the real source of the event doesn't provide any action or actionListener (e.g.)
//in this example inputText will process the event, when hitting enter on inputText
<ice:form>
<ice:inputText actionListener="#
//in this example again the inputText will process the event, when hitting enter on inputText
<ice:form actionListener="#{bean.formListener}">
<ice:inputText actionListener="#{bean.textListener}
" />
//in this example due to the absence of actionListener on inputText, the form will process the event, when hitting enter on inputText.
<ice:form actionListener="#
">
<ice:inputText />
Please see attached demos for more detail.
This scenario
//in this example again the inputText will process the event, when hitting enter on inputText <ice:form actionListener="#{bean.formListener}"> <ice:inputText actionListener="#{bean.textListener}" />
not works in http://anonsvn.icesoft.org/repo/icefaces/trunk/icefaces rev. 32567. Only form action listener is fired.
We've identified two main strategies for accomplishing this.
One strategy would be to modify each ice: component that can specify an action or actionListener, and make it so that where they decide if they are responsible for creating an ActionEvent, to check if an action/actionListener is specified on them, and if so create their own ActionEvent, and otherwise find their form, and if it's an ice:form, allow that to create an ActionEvent if it has an action or actionListener specified on it. This wouldn't work with third party components, and in the case of the h: components would result in the stock browser behaviour, instead of the ice:form specified one.
Another strategy would be to modify ice:form to detect when a full form submission has occurred, where none of its children had queued an ActionEvent, and then queue one itself, if it has an action/actionListener. This would involve overriding HtmlForm's queueEvent and processDecodes methods, so as to track if queueEvent had been called with an ActionEvent, while processDecodes was recursing through the child components, which is when they could enqueue an ActionEvent. At the end of processDecodes, if no child had enqueued an ActionEvent, then HtmlForm could do so itself, if it had an action/actionListener and if it was a full form submit. The problem here is that if the child component that does enqueue an ActionEvent is within a UIData or ICEfaces UISeries, then the ActionEvent will be wrapped inside another FacesEvent, which contains the UIData/UISeries row information. Even if we allow for inspection within ICEfaces' row events, that still doesn't cover the UIData wrapper event. Introspection could be added in that case. Still, any third party container component that uses event wrapping would break this feature.