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.
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'd like to show the names of Authors from a list called documentAuthorBeanToShow. I'm showing them in inputtexts and I'm showing a button to remove each Author showed. When I click the button I'd like to remove the Author from the list and remove it from the view automatically.
The problem is: When I click the button, I get the exception:
HTTP STATUS 500 - java.util.ConcurrentModificationException
I've checked some links here, and discovered I need to use the method Iterator.remove() to remove elements from a list while it's being iterated. But my problem is about JSF. I don't know how to use an iterator in the xhtml page. What's the best way to do that simple task I'm trying to?
EDITED - NOW USING Iterator.remove(). With the code below I don't get the exception described above. To have it working properly, it's just necessary to add the property described on the answer I choose.
Look at the code:
xhtml
<a4j:repeat value="#{editDocController.documentAuthorsBeanToShow}" var="author" >
<br />
<h:inputText value="#{author.name}" disabled="true" />
<h:commandButton type="submit" action="#{editDocController.removeAutor(author.uri)}" value="Remover" />
<br />
</a4j:repeat>
editDocController
public void removeAutor(String uri) {
Iterator<AuthorBean> itAuthorBean = this.documentAuthorsBeanToShow.iterator();
while(itAuthorBean.hasNext()) {
AuthorBean a = itAuthorBean.next();
if(a.getUri().equals(uri)) {
itAuthorBean.remove();
}
}
}
Thank you!
Use Iterator in your removeAutor() method.
public void removeAutor(String uri) {
Iterator<AuthorBean> iterator = documentAuthorsBeanToShow.iterator();
while (iterator.hasNext()) {
if(iterator.next().getUri().equals(uri)){
iterator.remove();
}
}
}
Maybe this can help you, I put the zone that you wanna refresh into a a4j:outPutpanel with an id, then change the commandButton for a4j:commandButton with an action and specify the zone that you wanna rerender, take a look at this poor example :
<a4j:outputPanel id="panellImatge">
<!-- here a4j:repeat...whatever-->
<a4j:commandButton styleClass="button" action = "yourControllertoremove" reRender="panellImatge"/>
</a4j:ouPutPanel>
I hope it helps.
So in you controller you remove the elements.
I'm working on a rich:datatable on a JSF page. The table can get pretty big and is paged with a rich:datascroller. Most of the columns are hardwired and will always be there, but then there are some optional columns based on additional values that need to be generated for each potential additional value. I've been able to make this happen easily enough. However, I'm running into a problem with filtering.
I'm using a filter on each column. It's placed in the header with the column label and sorting function. That much is working fine on each column, but I'm hitting a snag on filtering due to the way filtermethod works by default. Here's a quick example:
<rich:datatable id="thetable" value=#{backingBean.stuff} var="b">
<!-- First column, standard filter method, works just fine -->
<rich:column sortBy="#{b.field1}" filterMethod="#{filterBean.filterField1}">
<f:facet name="header">
<ui:fragment>
<h:outputText value="Field 1" />
<h:inputText value="#{filterBean.filterMap['Field1']}" />
</ui:fragment>
</f:facet>
#{b.field1}
</rich:column>
<c:forEach items="#{backingBean.extraStuff}" var="e">
<rich:column sortBy="#{b.getExtra(e)}" filterMethod="???">
<f:facet name="header">
<ui:fragment>
<h:outputText value="#{b.getExtra(e).description}" />
<h:inputText value="#{filterBean.filterMap['b.getExtra(e).code']}" />
</ui:fragment>
</f:facet>
#{b.getExtra(e).description}
</rich:column>
</rich:datatable>
The ??? will be covered shortly. As for the filter bean:
public class FilterBean {
public Map<String, String> filterMap = new HashMap<String, String>();
public boolean filterField1(Object current){
return ((BackingBean) current).contains(filterMap.get("Field1"));
}
}
It's fairly straightforward. The filter inputText binds to a preset string in the hashMap, which is retrieved in the method and used to filter so I don't need a separate field for every filter. This is working great, but I still need a separate method for each filter, which brings me to the ??? in the JSF code...
What I'd like to do is pass arguments to the filter method to account for the dynamic columns. In fact, I'd like to simplify the whole class with a single filter method and pass the mapped String in along with the field from the current object. However, this isn't working. I've tried:
filterMethod="#{filterBean.filterStuff(b, 'Field1')}"
but I wind up getting the filter string just fine, but null for the current object. I'm not sure what's going on. If I'm reading the dependencies in the project correctly, I'm using some pretty old versions of EL, JSF, JSP, etc, and I really have no way of changing that. The project does use Seam, though, and I've passed arguments successfully in EL before in this project. Is it just that EL 2.2 supports passing objects while older versions only supported primitives and Strings? Is there any way for me to make this happen or am I stuck without building a ton of extra stuff from the ground up?
Okay, looks like this might be possible with Seam, but it doesn't like iteration variables. I CAN pass the object if I refer to an index in the List from the backing bean, but that doesn't help as I have no way of telling it to search every row...
My use case is a bit different, but basically I had the same problem and found a working solution.
The use case: I have several XHTML-files with their backing-beans all offering a table with a list of entities of different types. In this table there are several columns for some attributes of the entities with the possibility to filter. Since the built-in filter does only a "starts-with" search and I need a more advanced one, I have to use the filterMethod.
But I did not want to mess up my backing-beans with hundreds of simple filter-methods doing all exactly the same (only with different attributes). So I was looking for a more generic way - and this is my approach:
In the backend, I created a new class named FilterMethodWrapper (for easier understanding I put it as nested static class here) and a method to access it:
import org.apache.commons.beanutils.PropertyUtils;
public class BackendBean
{
private String[] filterValues;
// initialize filterValues somewhere, add getter and setter
public static class FilterMethodWrapper
{
private final String fieldName;
private final String filterValue;
public FilterMethodWrapper(final String fieldName, String filterValue)
{
this.fieldName = fieldName;
this.filterValue = filterValue;
}
public boolean doFilter(Object current) throws ...
{
final String stringValue = (String) PropertyUtils.getSimpleProperty(current, fieldName);
// compare stringValue and filterValue somehow (e.g. contains)
// and return result
}
}
public FilterMethodWrapper getFilterMethodWrapper(String fieldName, int filterValueIndex)
{
return new FilterMethodWrapper(fieldName, getFilterValues()[filterValueIndex]);
}
}
And in the XHTMLs use it as follows:
<rich:column filterMethod="#{backendBean.getFilterMethodWrapper('username', 0).doFilter}" filterEvent="onkeyup" >
<f:facet name="header">
<h:panelGrid style="display:inline;">
<h:outputText value="Username"/>
<h:inputText value="#{backendBean.filterValues[0]}" />
</h:panelGrid>
</f:facet>
<h:outputText value="#{_item.username}" />
</rich:column>
Edit: I'm using JSF 1.2 and RichFaces 3.3.3.Final
Edit2: instead of writing the FilterMethodWrapper you could also use some Predicate-implementation and use the apply-method in the frontend (or you write your own Predicate-implementation according to this proposal which is more reusable than this FilterMethodWrapper)
There a several related question on this topic on SO and elsewhere, but I couldn't find a definitive answer on this specific question.
I have a p:dataTable and I want the possibility to click on a row and open a detail page (a new page, not a dialogue or window).
I have solved it this way (which I have from the primefaces website, for some reason it is no longer there: http://web.archive.org/web/20101001223235/http://www.primefaces.org/showcase/ui/datatableRowSelectionInstant.jsf):
<p:dataTable var="order" value="#{orderBean.orders}" selection="#{orderBean.selectedOrder}" selectionMode="single" rowKey="#{order.number}">
<p:ajax event="rowSelect" listener="#{orderBean.orderSelect}"/>
<p:column ... />
</p:dataTable>
The navigation is then executed in the bean:
public void orderSelect(SelectEvent event) {
ConfigurableNavigationHandler nh = (ConfigurableNavigationHandler)FacesContext.getCurrentInstance().getApplication().getNavigationHandler();
nh.performNavigation("orderDetail?faces-redirect=true");
}
My Question: is there a way of doing this just inside JSF without the help of a backing bean?
I am also asking because they removed the code exmaple from the primefaces site, which might be an indication that this is not the right way of doing something like that.
Wrap the cell(s) of interest in a simple <h:link>.
<p:column>
<h:link outcome="orderDetail">
...
</h:link>
</p:column>
Use if necessary CSS display:block on the link to let it span the entire cell. You can if necessary pass request parameters using a nested <f:param>.
since it is an ajax request, typically the request/response is used to re-render some components in the web page. What you could do is
<p:ajax event="someventofintrest" onsuccess="javascript:myjsmethod();"></p:ajax>
and
<p:remotecommand name="myjsmethod" action="#{mybean.mybeanmethod}" />
and in the backing bean
public String mybeanmethod(){
return "mynewpage"; // Navigate away to mynewpage.xhtml
}
HTH.
As I didn't find a really perfect solution, this is how I do it now.
I have now a "navigator" class like this
#Component
public class Navigator {
public void nav(String page) {
UIHelper.navigateTo(page);
}
}
And I call this class from my ajax event:
<p:ajax event="rowSelect" listener="#{navigator.nav('orderDetail')}"/>
As I said, not really perfect, but I like the fact that I don't have to write code in my backing bean. (Of course I have to write code for the Navigator, but that I can re-use.)
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