Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

preRenderView disables ajax

Tags:

java

jsf

jsf-2

<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.

like image 458
Menno Avatar asked Mar 06 '26 22:03

Menno


1 Answers

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:

  1. 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).

  2. 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").

See also:

  • commandButton/commandLink/ajax action/listener method not invoked or input value not updated - Point 5 applies to your case
  • @Inject to pass params to a CDI @Named bean via URL - Contains a concrete example of custom @HttpParam annotation as CDI substitution of JSF @ManagedProperty
  • ViewParam vs @ManagedProperty(value = "#{param.id}") - to understand the difference in its lifecycle better

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>.

like image 195
BalusC Avatar answered Mar 08 '26 10:03

BalusC



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!