3

I have a @ViewScoped @ManagedBean which creates an unique ID. This ID is rendered in a form like:

<h:form> 
   <h:outputText value="#{myBean.uid}" id="uid"/>
   <h:hiddenInput value="#{myBean.uid}" id="hiddenId" />  
   ....
  <p:commandButton actionListener="#{myBean.create}" ajax="true" update="@form" value="Create" /> 
</h:form>

So far so good. On first request the page is rendered correctly. After submitting the form and in the case of validation failure, the outputText is empty but the hidden input field keeps its variable. Any clue what I'd have to do to prevent this behavior and too let the outputText keep its state? I realized that the bean seems to be initialized after each ajax request. But then, why does the hidden input field keeps the old variable?

Here is the relevant code of my bean:

@ManagedBean(name = "myBean", eager = true)
@Stateful
@Model
@ViewScoped
public class MyBean implements Serializable {

  ...
  private String uid;
  ...

  @PostConstruct
  public void initWithData() {
      this.uid = UUID.randomUUID().toString();
  }

}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Felix
  • 4,213
  • 1
  • 21
  • 27
  • Hidden field is initialized because when you submit form, it sends value and it is set in your backing bean. To see real reason what is a problem you should show relevant parts of your managed bean code. – partlov Apr 09 '13 at 09:41
  • Hey partlov. Thanks. I will update the question. Anyways: why is the outputtext not updated. Both fields have the same values: #{myBean.uid} – Felix Apr 09 '13 at 09:54
  • Try using javascript to populate the outputText if there are no validation errors. – Johny T Koshy Apr 09 '13 at 10:14
  • There are validation errors, which is fine. But in this case, the outputText should remain the correct value and not be rendered empty. – Felix Apr 09 '13 at 10:15
  • If you are going for javascript solution then avoid executing outputText. – Johny T Koshy Apr 09 '13 at 10:25
  • @Johny There are a lot of input fields to be submitted in my whole form. How can I explicitly exclude the update of one field? – Felix Apr 09 '13 at 10:28
  • update individual components? – Johny T Koshy Apr 09 '13 at 10:34
  • Hm, could word, but will lead to mistakes when enhanced in the future. Every new form field would need to be added to the update attribute. Not good in my opinion. So I am looking for a more future-proof solution. – Felix Apr 09 '13 at 10:36

2 Answers2

3

JSF input components have 3 places where the value (the state) is stored:

  1. Submitted value (the raw unconverted/unvalidated String request parameter value).
  2. Local value (the successfully converted/validated object value, stored inside component itself).
  3. Model value (when the entire form is successfully processed, stored as bean property)

JSF processes input components as follows:

  • Get HTTP request parameter value by component's client ID and store it as submitted value.
  • If conversion/validation succeeds, set it as local value and set submitted value to null.
  • If entire form is successfully processed, set it as model value and set local value to null.

JSF renders values of input components as follows:

  • If submitted value is not null, display it.
  • Else if local value is not null, display it.
  • Else display model value.

So, in your particular case of a general validation failure, JSF is for that hidden input component just redisplaying the local value instead of the model value. If you want to achieve the same with the output text, I think best is to just do the same as JSF:

<h:outputText value="#{empty hiddenId.submittedValue ? empty hiddenId.localValue ? hiddenId.value : hiddenId.localValue : hiddenId.submittedValue}" />
<h:inputHidden id="hiddenId" binding="#{hiddenId}" value="#{myBean.uid}" />  

Alternatively, you could just use a read only input and remove the border by CSS if necessary:

<h:inputText id="hiddenId" value="#{myBean.uid}" readonly="true" style="border: none;" />  

As to your bean, I'm not sure what's happening there as this class seems to be extremely tight coupled. I'd rather split it into 3 classes: one real backing bean, one stateless service and one model entity. Further, you should also make sure that you aren't binding view build time tags or attributes to a property of a view scoped bean. Otherwise it will indeed guaranteed be reconstructed on every single request.

See also:


By the way, the eager=true has only effect in @ApplicationScoped beans.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks for your answer. I like the solution with inputText and readonly="true". Can you tell me though, why @PostConstruct is being called each time I make a POST request? How can I avoid this? – Felix Apr 09 '13 at 13:47
  • You're welcome. As to the view scope reconstruction problem, not sure. It should work fine on a `@ViewScoped @ManagedBean` (without all those other annotations indicating tight coupling; I can't answer reliably from top of head as I've never used this strange construct), provided that you aren't binding any view build time tags/attributes to a property of a view scoped bean. See also http://stackoverflow.com/a/3343681 and http://stackoverflow.com/a/8804588 – BalusC Apr 09 '13 at 13:50
  • Thanks. I am not sure if I understand what you mean by strange construct. Whas makes it strange? What should I change to make it not strange? – Felix Apr 09 '13 at 13:56
  • The thing is: All I want to do is presetting a value, which cannot be changed. If the form is submitted this value is used and processed as well...Simple thing, but this JSF stuff is kind of killing me. – Felix Apr 09 '13 at 14:00
  • 1) By splitting the tightly coupled class as explained in my answer. 2) JSF is easier to understand if you understand how basic HTTP, Servlets and HTML (and JavaScript/ajax) work as JSF is basically sitting on top of that, abstracting away all the boilerplate. – BalusC Apr 09 '13 at 14:06
  • It was decoulped already. Seemead as if the `@Stateful or/and `@Model annotation was causing this weird behaviour. After removing these annotations everything is working fine. Thanks again for you support. – Felix Apr 09 '13 at 15:30
  • If it's already decoupled, then what were those annotations doing there in first place? :) It indicated that you're reusing exactly the same class for 3 completely different purposes (for which theoretically 3 different instances would have been created which do not share their properties with each other, resulting in unclear behavior depending on how exactly you're referencing/using those instances, which wasn't clear from the question). In any case, glad your problem is solved and you're welcome. – BalusC Apr 09 '13 at 15:32
  • I guess it was because of copying some example. New technologies. Lots of trial an error :) – Felix Apr 09 '13 at 15:38
1

The problem is probably the @Model stereotype (was, as you already removed it). It combines @Named and @RequestScoped and makes the bean a CDI request scoped bean. CDI managed beans should be resolved before JSF managed beans and therefore the @ViewScoped has no effect (it creates a JSF managed bean).

Michi
  • 1,595
  • 10
  • 11