Why form submission doesn't work?
I have next form:
<h:form>
<h:selectManyListbox value="#{reports.selectedCategories}"
converter="#{categoryConverter}">
<f:selectItems value="#{reports.categories}"/>
</h:selectManyListbox>
<h:commandButton value="Submit" action="#{reports.action}" />
</h:form>
I use custom converter to transform Category to string and vice versa:
public class CategoryConverter implements Converter {
@Autowired
private CategoryService categoryService;
public Object getAsObject(FacesContext context, UIComponent component, String value) {
System.out.println ("CONVERTER: GET AS OBJECT");
return categoryService.findByName (value);
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
System.out.println ("CONVERTER: GET AS STRING");
return ((Category) value).getName ();
}
}
And finally my backing bean code:
public class ReportsController {
private Category[] selectedCategories;
public void setSelectedCategories (Category[] categories) {
System.out.println ("SET CATEGORIES");
this.selectedCategories = categories;
}
public Category[] getSelectedCategories () {
System.out.println ("GET CATEGORIES");
return selectedCategories;
}
public void action () {
System.err.println ("ACTION");
}
....
//methods for accessing data
}
When I start my app, I see correct listbox with 2 items and a button "Submit". But when I press submit nothing happens. The only output I see is:
GET CATEGORIES
CONVERTER: GET AS STRING
CONVERTER: GET AS STRING
Neither setSelectedCategories () nor action () methods are ever called. And I don't understand why. Any suggestions?
UPD: the problem is even more stupid: when I commented all stuff related to Category class (i.e. my selectManyListbox, my converter, all related setters and getters) and left only method action, it still isn't being called.
<h:form>
<h:commandButton value="Submit" action="#{reports.action}" />
</h:form>
UPD2: here is the output from phase lis开发者_StackOverflowtener
START PHASE RESTORE_VIEW 1
END PHASE RESTORE_VIEW 1
START PHASE APPLY_REQUEST_VALUES 2
END PHASE APPLY_REQUEST_VALUES 2
START PHASE PROCESS_VALIDATIONS 3
END PHASE PROCESS_VALIDATIONS 3
START PHASE UPDATE_MODEL_VALUES 4
END PHASE UPDATE_MODEL_VALUES 4
START PHASE INVOKE_APPLICATION 5
END PHASE INVOKE_APPLICATION 5
START PHASE RENDER_RESPONSE 6
GET STR
END PHASE RENDER_RESPONSE 6
It seems that all phases worked fine but actually it's not true. I'm a bit confused.
When a JSF form submit fails, then you need to make sure of the following:
UICommand
components must be nested in anUIForm
component.<h:form> <h:commandButton value="submit" action="#{bean.submit}" /> </h:form>
You cannot nest multiple
UIForm
components in each other, this is prohibited by HTML specification. Watch out with included pages/subviews, those accounts up in the component tree! if thejsp:include
is already inside ah:form
, you need to remove theh:form
from the include page!<h:form> <h:form> <h:commandButton value="this will not work" action="#{bean.fail}" /> </h:form> </h:form>
No validation/conversion error should have been occurred (use
h:messages
to get them all).<h:form> <h:commandButton value="submit" action="#{bean.submit}" /> <h:messages /> </h:form>
If
UICommand
components are nested in anUIData
component (e.g.h:dataTable
), be sure that exactly the same DataModel (the object behind thevalue
attribute) is preserved. Easiest way to test this is placing the bean in session scope. If that fixes the problem, then you need to review the data loading logic.The
rendered
anddisabled
attributes of the component and all of the parent components should not evaluate negatively. Easiest way to test this is placing the bean in session scope. If that fixes the problem, then you need to review the code responsible for this.Be sure that no
PhaseListener
or anyEventListener
has changed the request lifecycle to skip the invoke action phase.Be sure that no
Filter
/Servlet
in the same request chain has blocked the request of theFacesServlet
somehow.
As to your actual problem; you didn't get PropertyNotFoundException
s or similar and your PhaseListener
has executed all appropriate lifecycle phases nicely, so I think that your problem is actually caused by 2. Nested forms.
When a JSF form "doesn't work", this is usually an indication that something (usually a component, validator or converter) has instructed the lifecycle that some of the input is invalid and that it should stop processing and re-render the view. This prevents junk data getting into your model and stops your business logic from acting on invalid input.
alt text http://java.sun.com/javaee/5/docs/tutorial/backup/update3/doc/images/jsfIntro-lifecycle.gif Figure from JEE5 Tuorial
This will be down some way to the configuration of your view and the logic of any code you've plugged into it. As Slartibartfast says, adding a <h:messages />
tag to the view will often inform you of which components triggered this response. Configuring a PhaseListener is often useful during debugging to tell you which phases completed during a request.
public void action () {
System.err.println ("ACTION");
}
This action method should return a String
:
public String action () {
System.err.println ("ACTION");
return null;
}
Some implementations tolerate a void
return type, but it contravenes the spec. The action
attribute must be bound to an application action as defined in the spec:
Application Actions
...
- The method must be public.
- The method must take no parameters.
- The method must return Object.
The action method will be called by the default ActionListener implementation, as described in Section 7.1.1 “ActionListener Property” above. Its responsibility is to perform the desired application actions, and then return a logical “outcome” (represented as a String) that can be used by a NavigationHandler in order to determine which view should be rendered next.
Because under "converter" you should provide converter name as is defined in faces-config.xml:
<converter>
<converter-id>myConverter</converter-id>
<converter-class>com.example.converters.MyConverter</converter-class>
</converter>
also having <h:messages/>
in the form is usually helpful in this situations, because you see what went wrong.
精彩评论