Elegant handling of attributes without a session bean - java

I'm working on an application using JSF 2.0 and Richfaces 4, that consists of many tables that display elements and of course, the usual View/Edit/Delete options. After some SO browsing and Google search I've decided to post a question because the answers I found did not solve my problem.
Right now, and going straight to the point, my application is having issues when handling certain attributes that are stored in request beans and, on certain points, are lost due to successive requests.
For example, when I want to edit an object, the object is sent (f:propertyActionListener) to a request bean that displays the data on a form, then it is discarded as that request ends. When saving, a new object is created and the attributes on the form are setted to it and the item gets saved instead of updated, since it has no id (JPA + Hibernate).
I've investigated many options and this is what I've did so far and the results:
f:param + h:link or h:commandLink: With #ManagedProperty the param is null, and I can't find it on the Context to look it up through JNDI.
f:setPropertyActionListener + h:commandLink + Request Bean: Works... but I'm losing some data. The form that displays the data has some conditionally rendered fields and I can't hold that info, so the form is messed if the validation phase finds invalid data.
f:viewParam + h:commandLink + View Scoped Bean: Weird stuff here. This one doesn't directly work because the bean seems to get discarded before rendering the form, because the form is rendered with no information since the bean is clean.
Using a session bean: Works like a charm, but I don't want to make a session bean for every form just because I'm still learning things about the JSF lifecycle, I want to do it the proper way.
If I want to keep the Request session approach, is there a way to store a parameter (either an object or a plain string) and obtain later on a request bean?.
Dunno if this helps but I'm using a master page through ui:insert and ui:define.

Use a view scoped bean. It should work. The problems which you describe there suggests that you're binding it to JSTL tags or id or binding attributes. You should not do that on a view scoped bean. See also #ViewScoped fails in tag handlers. Another possible cause is that you're using CDI's #Named to manage the bean instead of JSF's #ManagedBean. That would also explain why #ManagedProperty doesn't work in one of your attempts as it also requires the bean to be managed by JSF's #ManagedBean.
As to the master-detail page approach, use a <h:link> with <f:param> in the table page to create view/edit links in the master page.
E.g. user/list.xhtml
<h:dataTable value="#{userList.users}" var="user">
<h:column>#{user.id}</h:column>
<h:column>#{user.name}</h:column>
<h:column>
<h:link value="Edit" outcome="edit">
<f:param name="id" value="#{user.id}" />
</h:link>
</h:column>
</h:dataTable>
The bean can be just request scoped.
Then, in the defail page, which is in this case an edit page, use <f:viewParam> to convert, validate and set the id as User.
E.g. user/edit.xhtml
<f:metadata>
<f:viewParam name="id" value="#{userEdit.user}"
converter="#{userConverter}" converterMessage="Bad request. Unknown user."
required="true" requiredMessage="Bad request. Please use a link from within the system." />
</f:metadata>
<h:messages />
<h:link value="Back to all users" outcome="users" />
<h:form id="user" rendered="#{not empty userEdit.user}">
<h:inputText value="#{userEdit.user.name}" required="true" />
...
<h:commandButton value="Save" action="#{userEdit.save}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
</h:form>
Use a #ViewScoped bean to hold the data, service and action methods:
#ManagedBean
#ViewScoped
public class UserEdit {
private User user;
#EJB
private UserService service;
public String save() {
service.save(user);
return "users";
}
// Getter+setter.
}

Related

Passing information between JSF pages

I am developing an application where I have to show data from the database based on User Input (let's say field empid for employee id and I show some information from database based on this input). I am using JSF, PrimeFace and Hibernate.
Now I have the index.xhtml page where I am taking input the empid. After that, I have to redirect to another page page2.xhtml.
Here is a snippet from index.xhtml:
<h:form>
<p>Employee ID:
<p:inputText value="#{userBean.newuser.empid}" required="true"/></p>
</h:form>
<br />
<h:form>
<p:commandButton action="page2?faces-redirect=true" value="Enter" />
</h:form>
But after re-directing to page2.xhtml, the empid input from index.xhtml page is lost. I tried to read a few posts on this but was not able to understand. Please help. I am sorry if it is a repeated question.
I assume that viewscopes of managed bean in which you are insterested in are #RequestScope ? You could specify more wide view scope for a managed bean, so it won't be destroyed after each request. However #RequestScope is really good when want to minimize session scope bloat. To stay with this session scope you could use flash object which was introduced with JSF 2.0. Thanks to this object you can transfer data between requests. You could put following code snippet :
ExternalContext.getFlash().put("empid", newuser.getEmpid);
in your userBean. After redirect you could retrieve that value on jsf page in the following way :
#{flash.empid}
Or in your managed bean :
ExternalContext.getFlash().get("empid");
More about flash with example can be found in this post or in JSF 2 Flash documentation

ui:include dependent on viewParam

I have a page where I want to include a part of the page (footer in this instance) dependant on values given from a view parameter.
I have my ViewScoped backing bean initializing on preRenderView
<f:metadata>
<f:viewParam name="racecode" value="#{displayResults.racecode}" />
<f:event type="preRenderView" listener="#{displayResults.init}" />
</f:metadata>
This queries the database to get the name of the footer to be included. This then, is used in this fashion :
<h:panelGroup id="customFooter" display="block">
<ui:include src="#{displayResults.customFooter}" />
</h:panelGroup>
This always gives me a missing page. But if I enter the page name manually it works. Same if I replace the ui:include with an h:outputText.
I understand that it has something to do with the phases of JSF and that at the time the ui:include is done, the value is not set yet. (reading up and better understanding the phases is something on my TODO list).
The question remains. How can I get something of the sort done. Have a bean use the viewParam, query the database and use that value in a ui:include?
#wemu has already explained the cause. The <ui:include src> is evaluated before init() method is called. His proposed <f:phaseListener> solution is however clumsy.
Just use #ManagedProperty/#PostConstruct on a #RequestScoped bean.
#ManagedProperty("#{param.racecode}")
private String racecode;
#PostConstruct
public void init() {
// ...
}
PreRenderView listeners are called within the RenderResponsePhase, before components are rendered BUT AFTER the TagHandlers are called. This means that TagHandlers will NOT see data initialized within a PreRenderView event.
If you are using a <ui:include value="#{myBean.myViewId}" /> to dynamically switch an include you can't use a PreRenderView event listener to set the myViewId property of myBean.
If you need to do that use a <f:phaseListener>.

Is there a way I can invoke something per request in a view scoped bean in JSF?

I have a basic CRUD view-scoped bean. Within the setter methods I am performing some data-specific validation, which build a detailed error message for each setter if an error occurs in any of them.
This works fine, but I would like to empty this error message on each request and I have no idea how I would do that.
preRenderView won't cut it, because this error message needs to be rendered as well. Something like a postRenderView would be ideal.
You shouldn't perform validation in setter methods and you shouldn't store validation messages in the backing bean. Your whole problem is just caused by bad design and not utilizing JSF provided validation facilities.
Just utilize JSF provided validation facilities instead of working completely around it and all your problems as described so far will disappear. You can use several of the JSF builtin validators such as required="true", validator="javax.faces.XxxValidator, <f:validateXxx> tags, etc on input components. You can create a custom validator by implementing Validator interface and giving it an unique validator ID which you use in validator="myValidator" or <f:validator validatorId="myValidator">.
When using JSF standard validation, any validation error will be thrown as a ValidatorException with a FacesMessage in the request scope which would be shown in a <h:message> associated with the component. This way the messages will "automagically" disappear in the subsequent requests.
Here's a very basic kickoff example:
<h:form>
<h:inputText id="foo" required="true" requiredMessage="Enter this!" />
<h:message for="foo" />
<h:commandButton value="Submit" />
</h:form>
See also:
Is there how to validate into rich:calendar if the date selected is before a specific date?
How to create JSF form with AJAX data validation
How to show multi error message in jsf while validation is in EJB?
How validate two password fields by ajax?
etc...

How can I reset JSF UIInput components to their managed bean values

I want to reset JSF inputs to their original managed bean values after validation failed.
I have two forms inside the same page - the first form has a commandLink to initialize the second form. The second form is rendered as a dialog whose visibility is toggled through jQuery - for the purpose of this exercise, though, I can illustrate just with two forms on the same page. Also, while I'm using PrimeFaces 2.2.x in my app, the same behaviors appear with regular h:commandLink as well.
The issue I'm having is:
click link in first form to initialize second form
submit invalid values in second form
click link in first form again to initialize second form - invalid values still there and/or UIInput state is still invalid.
For example - take the following form
<h:form id="pageForm">
<h:commandLink actionListener="#{testBean.initialize}">Initialize, no execute
<f:ajax render=":dialogForm"/>
</h:commandLink>
<br/>
<h:commandLink actionListener="#{testBean.initialize}">Initialize, execute=#this
<f:ajax execute="#this" render=":dialogForm"/>
</h:commandLink>
</h:form>
<h:form id="dialogForm">
<h:messages/>
String property - Valid: <h:outputText value="#{property.valid}"/>
<br/>
<h:inputText id="property" binding="#{property}" value="#{testBean.property}">
<f:validateLength minimum="3"/>
</h:inputText>
<br />
Int property - Valid: <h:outputText value="#{intValue.valid}"/>
<h:inputText id="intValue" binding="#{intValue}" value="#{testBean.intValue}">
<f:validateLongRange maximum="50" />
</h:inputText>
<br/>
<h:commandLink actionListener="#{testBean.submit}">
Submit
<f:ajax render="#form" execute="#form"/>
</h:commandLink>
<h:commandLink actionListener="#{testBean.initialize}">Initialize, execute=#this
<f:ajax execute="#this" render="#form"/>
</h:commandLink>
</h:form>
Bean class:
#ManagedBean
#ViewScoped
public class TestBean {
private String property = "init";
private Integer intValue = 33;
// plus getters/setters
public void submit() { ... }
public void initialize() {
intValue = 33;
property = "init";
}
}
Behavior #1
click either "Initialize" link on the pageForm
inputs get initialized to "init", "33"
now submit something invalid for both fields like "aa", "99"
now click any of the "initialize" links again (they all seem to behave the same - makes no difference whether it's in the same form or different, or whether I have specified execute="#this" or not.)
Result => UIInput.isValid() = false, both values reset though ("init", "33").
Expected => valid = true (or is this not reasonable to expect?)
Behavior #2
click either "Initialize" link on the pageForm
inputs get initialized to "init", "33"
now submit something invalid for the text field but valid for the int field ("aa", "44")
now click any of the "initialize" links again
Result => "init", valid=false; 44, valid=true
Expected => "init", valid=true; 33, valid=true
I have also looked at:
JSF 2 - Bean Validation: validation failed -> empty values are replaced with last valid values from managed bean
and
How can I populate a text field using PrimeFaces AJAX after validation errors occur?
The suggestion to explicitly reset the state of UIInputs with resetValue does work, but I'm not happy with it.
Now, I sort of understand why the isValid is not resetting - my understanding of the JSF lifecycle is that once a value is submitted to a component, isValid is not reset until the component is successfully submitted and validated and the Update Model Values phase sets the bean value. So there may be no way around explicitly resetting the valid state in this case, since I want to use #{foo.valid} for conditional CSS styling.
What I don't understand, though, is why the components that successfully validated are not re-initializing from the bean. Perhaps my understanding of the JSF lifecycle is slightly off?
I understand the rules layed out in the answer to How can I populate a text field using PrimeFaces AJAX after validation errors occur? as they pertain to an individual component but not to the form as a whole - i.e., what happens if a component succeeds validation but the validation overall fails?
In fact, there may turn out to be no better way than explicitly calling resetValue on components. In my case, all of the dialogs are in the same big JSF view tree with the underlying page that opens them. So from JSF's perspective, the same view component state including invalid input values should be preserved until we navigate away from the view, as it has no visibility into how we're toggling display attributes client-side.
The only other thing that might work is if the components that make up the dialog are actually not rendered in the JSF view tree unless they're visible. In my case, they're always rendered, using CSS to toggle visibility.

Seam & JSF - misunderstanding <h:inputHidden />?

I've written an e-commerce web application using Seam 2.2, JPA, and JSF that, of course, contains product search functionality. To accomplish this, I've created a class called SearchForm that contains the various parameters used for searching (start index, maximum number of results, 'and' terms, 'or' terms, etc.) I've also got a web action -- ProductSearchAction -- that uses the SearchForm object to pull the entries from the database. It looks something like this:
#Name("searchForm")
#AutoCreate
#Scope(ScopeType.CONVERSATION)
public class SearchForm {
private int startIndex = 0;
private int maxResults = 20;
...
}
#Name("productSearchAction")
#AutoCreate
#Scope(ScopeType.CONVERSATION)
public class ProductSearchAction {
#In
private SearchForm searchForm = null;
#Out
private List<Products> products = null;
...
public void searchProducts() {
...
}
...
}
In my JSF, I display the list of products enclosed within an <h:form />, with 2 <h:commandLink /> links for paging forward and backward through the results. Since I don't create a conversation for each search, I'm trying to pass state to the ProductSearchAction and SearchForm objects through the use of <h:inputHidden /> hidden fields. I've got fields like this in my page:
<h:form>
...
<h:inputHidden value="#{searchForm.maxResults}" />
<h:inputHidden value="#{searchForm.startIndex}" />
<h:inputHidden value="#{searchForm.andTerms}" />
...
<h:commandLink action="next" value="Next" />
<h:commandLink action="previous" value="Previous" />
</h:form>
My understanding of <h:inputHidden /> is that it will populate the appropriate values within SearchForm, which will then be made available to ProductSearchAction.searchProducts(). When I view the HTML source I see the hidden parameters being set within the HTML. However, when I click "next" or "previous" which take me to the searchProducts() action none of the values are set.
Am I misunderstanding how <h:inputHidden /> works? What do I need to do to pass these values to my search action? Is there a better way to accomplish my goal? Is it a Seam Scope issue? I'd REALLY appreciate any help you can give.
Based on your comment it sounds like you are using h:inputHidden correctly, and that the problem must lie in the JSF bean scoping.
The beans are behaving as if they are request scope. When you fire of a h:commandLink, the page re-renders and posts the hidden inputs back, and then those posted values are not available after the navigation result ("next" or "prev") forwards to another page.
In all likelihood the #Scope(ScopeType.CONVERSATION) is not behaving as you expect it to. I am not a Seam expert, but from a quick scan of the documentation it looks like Seam treats each individual HTTP request as a "conversation" unless otherwise indicated. So that would explain why the values reset when you click the commandLink. You probably need to demarcate a long-running conversation with the #Begin/#End annotations.
http://seamframework.org/Community/ConversationExample

Categories