简体   繁体   中英

Re-execute f:viewAction when ViewScoped bean is recreated following a POST request

Environment: JSF 2.2 with Mojarra 2.2.12 & CDI ViewScoped beans & javax.faces.STATE_SAVING_METHOD set to client .

In order to properly initialize my bean thanks to <f:viewParam ... /> , I would like to (re-)execute <f:viewAction action="#{bean.onLoad}" /> when my ViewScoped bean is recreated (view was pushed out from the LRU, cf. com.sun.faces.numberOfLogicalViews ) following a POST request.

<f:metadata>
    <f:viewParam maxlength="100" name="name" value="#{bean.file}" />
    <f:viewAction action="#{bean.onLoad}"  />
</f:metadata>

<o:form includeRequestParams="true">
     <!-- action can only work if onLoad has been called -->
     <p:commandButton action="#{bean.action}" />
</o:form>

Any ideas?

Notes:

  • I'm aware of postBack="true" but it's not suitable as bean.onLoad() would be called on every POST request.
  • I cannot call onLoad() in @PostConstruct method because values have not been set by viewParam yet (cf. When to use f:viewAction versus PostConstruct? ).

I'm aware of postBack="true" but it's not suitable as bean.onLoad() would be called on every POST request.

You can just use EL in onPostback attribute wherein you check if the model value and/or request parameter is present.

If the model value is required, then just check if it's present or not:

<f:metadata>
    <f:viewParam maxlength="100" name="name" value="#{bean.file}" required="true" />
    <f:viewAction action="#{bean.onLoad}" onPostback="#{empty bean.file}" />
</f:metadata>

If the model value is not required, then check the request parameter too:

<f:metadata>
    <f:viewParam maxlength="100" name="name" value="#{bean.file}" />
    <f:viewAction action="#{bean.onLoad}" onPostback="#{empty bean.file and not empty param.name}" />
</f:metadata>

I cannot call onLoad() in @PostConstruct method because values have not been set by viewParam yet.

Given the presence of <o:form> in your snippet, I see that you're using OmniFaces . The very same utility library offers a CDI @Param annotation for the very purpose of injecting, converting and validating HTTP request parameters before the @PostConstruct runs.

The entire <f:viewParam><f:viewAction> can therefore be replaced as below:

@Inject @Param(name="name", validators="javax.faces.Length", validatorAttributes=@Attribute(name="maximum", value="100"))
private String file;

@PostConstruct
public void onLoad() {
    if (!Faces.isValidationFailed()) {
        // ...
    }
}

Or if you've Bean Validation (aka JSR303) at hands:

@Inject @Param(name="name") @Size(max=100)
private String file;

@PostConstruct
public void onLoad() {
    if (!Faces.isValidationFailed()) {
        // ...
    }
}

Though I havent been struggeling with the numberOfLogicalView I often need to reinitialize beans with a long living scope. Eg a usecase is to (re-) load some detail data from database when the user clicks on some entity in a table-view. Then I simply use some initialized -flag, eg

@SomeLongLivingScope
public class MyBean {

    private boolean initialized = false;

    public void preRenderView() {
        if ( initialized ) return;
        try {
             do_init();
        } finally {
            initialized = true;
        }
    }

    public void someDataChangedObserver( @Observes someData ) {
        ...
        initialized = false;
    }
}

Using this pattern you can use your viewAction with postBack="true" .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM