0

This JSF1 code has me totally puzzled for hours. The basic setup is this page displayed with Seam2:

<h:form encType="multipart/form-data">
  <rich:dataTable value="#{results}">
    ...
  </rich:dataTable>

  <h:selectOneMenu value="#{contact.type}">
    <s:selectItems value="#{contactTypes}" var="t" label="#{t.label}" />
    <s:convertEntity />
    <a4j:support event="onchange" reRender="submitControls" />
  </h:selectOneMenu>

  <h:selectOneMenu value="#{template}">
    <s:selectItems value="#{allTemplates}" var="t" label="#{t.label}" />
    <s:convertEntity />
    <a4j:support event="onchange" reRender="submitControls" />
  </h:selectOneMenu>

  <a4j:outputPanel id="submitControls" layout="block">
    <a4j:outputPanel rendered="#{null != results and results.size gt 0 and ('ONE' == contact.type.label or template != null)}">
      <h:commandButton value="submit" action="#{manager.generate}" />
    </a4j:outputPanel>

    <h:outputText value="Search first" rendered="#{results == null or results.size == 0}" />
    <h:outputText value="Select template first" rendered="#{'ONE' == contact.type.label and template == null}" />
  </a4j:outputPanel>
</h:form>

Obviously, the original page is a bit larger. What has me scratching my head is that if I don't change contact.type (leave it at a default selected by the backing bean) the form submits fine. If I switch the type to ONE this correctly renders the "Select template first" text instead of the submit control. Restoring the submit button by selecting another type re-produces the <input> BUT without the onclick handler that was there when the form was first rendered.

Now a click on the <h:commandButton> sends a request to the server but does NOT trigger the associated action. However, it now restores the onclick handler and a second click triggers a proper submit.

I'm at a loss why this is so. Any suggestions?

EDIT: moving the rendered attribute to the button results in the same behavior (even if it did work, the original panels contain more controls that share the same condition, so they do serve a purpose)

EDIT2: I've just tested that simply re-adding the "lost" onclick handler (via firebug) that gets rendered on the submit button makes the action work as intended. I'm beginning to suspect a bad interaction between richfaces and the trinidad libs also included in this project (but not used on this page).

mabi
  • 5,279
  • 2
  • 43
  • 78
  • How did you verify that the request was sent to the server? firebug? Try putting the conditional render on the command button itself, as against the parent outputPanel (in which case even that output panel becomes unnecessary) – kolossus Jan 04 '13 at 04:08
  • @kolossus Yeah, with firebug I see a POST to the server. The response contains the button with it's onclick handler and the actual submit can happen. – mabi Jan 04 '13 at 14:02

2 Answers2

1

It's a safeguard against tampered/hacked requests. Otherwise a hacker would be able to invoke actions s/he isn't allowed to invoke by just editing the sent HTTP request parameters accordingly that the non-rendered (e.g. due to missing "ADMIN" role) command button will be invoked.

You need to make sure that you prepare the same model (managed bean instance with all properties responsible holding the conditions behind rendered attribute) during the HTTP request of processing the form submit as it was during the HTTP request of displaying the form. In JSF2, this is easy achievable by placing the bean in the view scope instead of the request scope. The view scope lives as long as you're interacting with the same view. In JSF1, you'd need to grab a 3rd party framework tag like Tomahawk's <t:saveState> or RichFaces' <a4j:keepAlive> in order to simulate the JSF2 view scope.

<a4j:keepAlive beanName="results" />

The same story applies to disabled attribute by the way.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Ew, I suspected as much. Thing is, the variables results, contact and template are outjected from two different beans (results as a @DataModel from an extra bean). However, the beans holding the fields have seam's conversation scope. Am I correct in suspecting that this doesn't cover AJAX requests (with it not POSTing the conversation id)? This still doesn't explain why the submit button loses it's onclick handler... – mabi Jan 07 '13 at 09:21
  • Forgot to say: I've added for the beans that are at play here, but the problem persists. – mabi Jan 07 '13 at 10:07
0

I think that with the rendered attribute and anything inside you have to take care that the evaluation of it is the same on the initial request AND the submit. It may change just before the render phase but if its not the same during application invoke it will most likely ignore the action if in this phase the button would not be rendered.

As far as i remember this happend for me mostly when the rendered expression uses something like an entity attribute that will be changed during the apply request values phase already.

Martin Frey
  • 10,025
  • 4
  • 25
  • 30