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
Related
I am building a JSF application. I defined the GUI and did the select statements query the database using select.
Now I must do the insert statements, but I don't know how to read the value of a JSF input component like <h:inputText> and send it to my bean which performs the insert.
Should <h:inputText> value be mapped through faces-config.xml, so I can have it in my Java code?
You need to put all <h:inputXxx>/<h:selectXxx> components in a <h:form> and bind their value attribute to a bean property via an EL expression like #{bean.property}, backed by a getter/setter pair. When properly set, JSF will automatically set the values in the bean when the form is submitted via a <h:commandXxx> component in the very same form. You can specify a bean action method in action attribute of the <h:commandXxx> component via an EL expression like #{bean.action}, which points to the bare method action(). All submitted values are available right away there the usual Java way.
Given this JSF form example with one input field and one select field:
<h:form>
<h:inputText value="#{bean.text}" required="true" />
<h:selectOneMenu value="#{bean.choice}" required="true">
<f:selectItem itemValue="#{null}" />
<f:selectItem itemValue="One" />
<f:selectItem itemValue="Two" />
<f:selectItem itemValue="Three" />
</h:selectOneMenu>
<h:commandButton value="submit" action="#{bean.submit}" />
<h:messages />
<h:outputText value="#{bean.result}" />
</h:form>
The following bean prints the submitted values to the stdout, proving that JSF has already set the values long before the moment you access it in the action method.
package com.example;
import javax.inject.Named;
import javax.enterprice.context.RequestScoped;
#Named // Use #javax.faces.bean.ManagedBean on outdated environments.
#RequestScoped // Use #javax.faces.bean.RequestScoped on outdated environments.
public class Bean {
private String text;
private String choice;
private String result;
public void submit() {
result = "Submitted values: " + text + ", " + choice;
System.out.println(result);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getChoice() {
return choice;
}
public void setChoice(String choice) {
this.choice = choice;
}
public String getResult() {
return result;
}
}
That's all. Turning the regular form into an ajax form is a matter of nesting a <f:ajax> in the command component as below.
<h:commandButton value="submit" action="#{bean.submit}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
You can find another example and valuable links at bottom of our JSF wiki page.
Do note that whatever you intend to do with the submitted values is beyond the responsibility of JSF. For example, manipulating it, passing into another class, saving it in database, etc. None of this all is related to JSF. It has as being a HTML form based framework already done its job of providing you the submitted values in flavor of usable Java variables. The remainder is up to you.
To investigate the next step, you should at this point just be doing as if you've already a bunch of prepared / hardcoded variables instead of a whole JSF based user interface. For example, in order save to the values in a database, people usually use a business service layer framework like EJB which in turn uses a persistence layer framework like JPA. Some people even use "plain vanilla" JDBC for that. For more links to concrete examples, start here: JSF Controller, Service and DAO.
I am trying to create a method that allows me to search via a JSF page with an inputText field. I intend on getting one or more entries from an arraylist, located in a managed bean, and displaying it on the JSF page once I hit submit.
I tried to draw inspiration from this question, but am stuck at creating the search method as I am a novice at Java:
JSF2 search box
In my JSF page I intend to have something like this, 'something' is that method that I'm missing:
<h:form>
<h:inputText id="search" value="#{order.something}">
<f:ajax execute="search" render="output" event="blur" />
</h:inputText>
<h2>
<h:outputText id="output" value="#{order.something}" />
</h2>
</h:form>
In my java file I have the following arraylist 'orderList', I just use strings:
private static final ArrayList<Order> orderList =
new ArrayList<Order>(Arrays.asList(
new Order("First", "Table", "description here"),
new Order("Second", "Chair", "2nd description here"),
new Order("Third", "Fridge", "3rd description here"),
new Order("Fourth", "Carpet", "4th description here"),
new Order("Fifth", "Stuff", "5th description here")
));
Hope this is sufficient information.
I guess I have to make some completely new methods from scratch, but I don't have much at the minute.
How would I go about tying these together? Any pointers would be greatly appreciated :)
~edit~
Here is my Order object for reference, I assume I use one of the getters here?
public static class Order{
String thingNo;
String thingName;
String thingInfo;
public Order(String thingNo, String thingName, String thingInfo) {
this.thingNo = thingNo;
this.thingName = thingName;
this.thingInfo = thingInfo;
}
public String getThingNo() {return thingNo;}
public void setThingNo(String thingNo) {this.thingNo = thingNo;}
public String getThingName() {return thingName;}
public void setThingName(String thingName) {this.thingName = thingName;}
public String getThingInfo() {return thingInfo;}
public void setThingInfo(String thingInfo) {this.thingInfo = thingInfo;}
}
First, you're going to need to do some work with java equality:
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op2.html
Searching the ArrayList (perhaps not the best structure for this, but it does work) would require writing a function to handle it, in the first example it uses the commandButton's action (executing a method). What you have won't do anything as it doesn't have anything to perform (no methods are called).
If you're just learning JSF and you're not familiar with Java I'd recommend keeping it simple until you learn the life cycle. It can be highly frustrating if you don't have a solid Java background.
However, to answer your question you would need to do something with a ValueChangeEvent as you don't have the command (as per the second half of his answer).
Your "search method" would be pure Java acting on the data-structure. A simple implementation could be:
public void searchByFirstValue(String search)
{
if(search == null || search.length() == 0)
return;
for(Order order : orderList)
{
if(order.getFirstValue().contains(search)) //java.lang.String methods via a regular expression
setSelectedOrder(order);
return;
}
}
This assumes that your Order object has a method getFirstValue() that returns a String. I'm also assuming that value is never null based on your constructor (There's a lot of potential pitfalls). Assuming you've registered the 'OrderBean' in the web.xml or you've used the ManagedBean annotations your JSF might look like:
<h:form>
<h:inputText id="search" valueChangeListener="#{orderBean.onChange}"> <!--Use the previous example -->
<f:ajax execute="search" render="output" event="blur" />
</h:inputText>
<h2>
<h:outputText id="output" value="#{orderBean.order}" /> <!-- this is the object from setSelectedOrder(order); -->
</h2>
</h:form>
Hopefully that points you in the right direction. Again, I'd stick to actions/actionListeners until you get the basics of the framework down. From there it is pretty easy to move to very complicated actions without a lot of work. The reason I say this is that they are easier to debug and very simple to understand.
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.
}
Unfortunately, I made a mistake of choosing JSF for an internet facing, high traffic application, now I am wondering as to how to improve the scalability of this JSF webapp.
I have a JSF page that displays a large no of items each of which may be commented upon.
Inorder to reduce the state & improve performance I am trying to reduce the no of forms /commandButtons on the page.
1. Through what ways can I reduce the component tree/ statefulness of JSF ? Do the plain html elements(that are mixed in between the jsf tags) also form part of component tree ? I dont know how component state saving has been helpful to my app since I have been following plain request/response model while designing my app, (may be it is helpful for just JSF's internal requirements)!?
2. I was thinking of an approach where instead of creating a separate <h:form> (each with a separate commandButton) for every item like below,
(Usual Approach)
<h:form> <!-- for each item a separately -->
<h:outputText value="Add comment"/>
<h:inputTextarea value="#{itemController.comment}" required="true"/>
<p:commandButton actionListener="#{itemController.addUserComment(123)}" value="Add" />
</h:form>
(Alternate Approach)
I am trying to make the above better by just putting a single remoteCommand for all the items & pass the required parameters to this remoteCommand.
<form>
<input id="item1_comment"/>
<button onclick="addComment(123, 'item1_comment');"/>
</form>
<script type="text/javascript">
function addComment(itemId, id) {
$('#comment_in').attr('value', $('#'+id).attr('value'));
$('#forItem_in').attr('value', itemId);
addComment_RC(); // call remoteCommand to show the content in dialog
}
</script>
<h:form prependId="false" > <!-- for all items, just single remoteCOmmand -->
<h:inputHidden id="comment_in" value="#{itemController.comment}"/>
<h:inputHidden id="forItem_in" value="#{itemController.forItem}"/>
<p:remoteCommand name="addComment_RC" process="#form" actionListener="#{itemController.addComment()}" />
</h:form>
Is it better to do it this way (or are there any issues with this approach)?
Performance issues in the situation you describe are often caused by the large number of EL expressions, That burdens the server.
One approach to tackle this issue is to compute the comments on the client side, and pass them all at once to the server. Thus reducing the number of comment EL expression to one or none, and use only one button.
Place all the elements in one form. The comments fields are not binded.
<h:form>
// first element
<h:outputText value=#{first element}
// first comment
<h:inputTextarea id="comment1"/> <-- notice there is no EL expression
But we use a unique id for each comment
// second element
<h:outputText value=#{second element}
// second comment
<h:inputTextarea id="comment2"/>
.
.
.
</h:form>
From here you could either
1. after each blur event in any of the comment fields, ajax the server and pass as parameters the comment and the id of the comment from which the ajax call was made. on the server update your model accordingly
Or You can also gather all the comments on the client side and send them to the server at one time.
2. When the user press the submit button call a js function to aggregate all the comments in a structure that you will be able to parse easily on the server side
(ie. "{c1,comment a};{c2,comment b};{c5=,comment e}...").
pass that string to the server, parse it and update your model accordingly.
3. after each blur event in any of the comment fields, call a js function that updates an hidden field.
<h:inputHidden value="{myClass.allComments}" />
when the user submits the form parse allComments and update your model accordingly.
EDIT:
To address the general performance issue I've added recommendations from an article that I found helpful speed up part 1 Speed up part 2.
Hope this helps
btw, I would recommend the first approach rather than the last two.
I have found a few good replies to similar content so far, but never something that solves my issue. I am trying to accomplish this in the best manner possible.
Within my application (JSF 2.0 running on Glasshfish), I have a list of events (let's call this the EventPage). It is possible to click on each event to then show a page of "results" (ResultPage), showing a list of people who have attended this event.
On the EventPage, the link is made as such :
<h:link value="#{event.eventName}" outcome="displayResults">
<f:param name="eventCode" value="#{event.eventCode}"/>
</h:link>
Then, on the outcome - displayResult, I have code such as this in my backing bean (inspiried by a similar):
#ManagedBean
#RequestScoped
public class DisplayResults {
#ManagedProperty(value="#{param.eventCode}")
...
This works well. The results are displayed in a Datatable. Now I want the ability to sort them. So I've followed this example : http://www.mkyong.com/jsf2/jsf-2-datatable-sorting-example/.
But, once I change the scope of my backing bean to be something else the "request", I can't use the ManagedProperty anymore. And thus am thinking I have to refer to something less elegant such as :
public String getPassedParameter() {
FacesContext facesContext = FacesContext.getCurrentInstance();
this.passedParameter = (String) facesContext.getExternalContext().
getRequestParameterMap().get("id");
return this.passedParameter;
}
Aslo reading on this forum I share the opinion that if you have to dig down into the FacesContext, you are probably doing it wrong.
SO: 1. Is it possible to sort a Datatable without refreshing the view? Only the datatable in question? 2. Is there another good solution to get the url parameter (or use diffrent means)?
Thanks!
Use <f:viewParam> (and <f:event>) in the target view instead of #ManagedProperty (and #PostConstruct).
<f:metadata>
<f:viewParam name="eventCode" value="#{displayResults.eventCode}" />
<f:event type="preRenderView" listener="#{displayResults.init}" />
</f:metadata>
As a bonus, this also allows for more declarative conversion and validation without the need to do it in the #PostConstruct.
See also:
ViewParam vs #ManagedProperty(value = "#{param.id}")
Communication in JSF2 - Processing GET request parameters