Reputation: 12621
<ui:composition template="/WEB-INF/templates/base.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:define name="metadata">
<f:metadata>
<f:event type="preRenderView" listener="#{resetPassword.loadUser}" />
<f:viewParam name="user" value="#{resetPassword.user}" />
</f:metadata>
</ui:define>
<ui:define name="content">
<h:form rendered="#{resetPassword.user != null}">
<h:panelGrid class="form_panel" columns="2" width="596px">
<h:outputLabel class="form_label" for="password" value="#{base['resetPassword.password']}" />
<h:inputSecret class="form_inputText" id="password" value="#{resetPassword.password}">
<f:ajax event="keyup" execute="password" listener="#{resetPassword.validatePassword}" render="scorePanel complexity" />
</h:inputSecret>
(...) // Other labels and inputfields, no ajax
</h:panelGrid>
</h:form>
</ui:define>
</ui:composition>
I have read about a lot of problems with preRenderView
in combination with ajax
-calls. The main problem I have come across is the postback
issue.Though, in my case, ajax
won't fire at all with the preRenderView
. Without the whole f:event
-tag, my ajax
-call does work correct.
resetPassword.loadUser()
will set resetPassword.user
, which is NOT null. I need to use the preRenderView
instead of @PostConstruct
in order to fill the f:viewParam
. This param is needed when the form is submitted.
Why does my ajax
-event break when <f:event type="preRenderView" />
is defined?
Note: <ui:insert name="metadata" />
in the template is located inside <h:head>
.
Update
As BalusC commented, the issue is not visible in this part of the code.
public void loadUser(ComponentSystemEvent event) {
if(!FacesContext.getCurrentInstance().isPostback()) {
user = (hash != null) ? userService.getByIdAndPwdResetHash(userId, hash) : null;
}
}
This code might return null
but doesn't (as my form is being loaded).
public void validatePassword(AjaxBehaviorEvent event) {
System.out.println("Ajax works"); // Just for testing purposes so far
}
I don't know what to add further, as this pretty much is all relevant code.
Upvotes: 3
Views: 1278
Reputation: 1108632
This construct will fail when the #{resetPassword}
is request scoped and the user
property is not initialized during bean's (post)construction.
JSF will during apply request values phase namely re-evaluate the rendered
attribute of an input component (this also includes all of its parent components) before processing the submitted value, as part of safeguard against tampered requests. In your case, the parent form is not rendered and thus all input components won't be processed.
You have basically 2 options:
Make the bean @ViewScoped
(or the CDI equivalent @ConversationScoped
). This way it lives as long as you're interacting with the same view (resp. conversation).
Perform the initialization of user
in (post)constructor. The pre render view is too late. As your bean is request scoped already, just use @ManagedProperty
(or its homegrown CDI equivalent; Google has results on "@HttpParam").
@HttpParam
annotation as CDI substitution of JSF @ManagedProperty
Unrelated to the concrete question, performing validation in an action(listener) method isn't the right approach. Instead of <f:ajax listener>
, use a normal Validator
which you reference by input component's validator
attribute or a nested <f:validator>
.
Upvotes: 4