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
Related
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>.
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
I have a problem with RichFaces and creating lists of links. If you attempt to use any type of commandLink inside a list (I've tried ui:repeat and rich:list) the action on that link is not called. I've also tried commandButton and the a4j variations of those. I'm using JSF 2, RichFaces 4 on Jboss 6.
<rich:list var="venue" value="#{searchManager.results}" type="definitions" stateVar="status">
<h:form>
<h:commandLink value="CLICK IT" immediate="true" action="#{score.selectVenue}" />
</h:form>
</rich:list>
The position of the form also doesn't matter.
<h:form>
<rich:list var="venue" value="#{searchManager.results}" type="definitions" stateVar="status">
<h:commandLink value="CLICK IT" immediate="true" action="#{score.selectVenue}" />
</rich:list>
</h:form>
If I just have the link by itself (no list) it works.
Any help would be greatly appreciated.
When you click a command link or press a command button to submit a form, JSF will during the apply request values phase scan the component tree for the command link/button in question so that it can find the action expression associated with it, which is in your case #{score.selectVenue}.
However to be able to ever reach that, you would need to ensure that #{searchManager.results} returns exactly the same list as it did when the form was displayed. Because with an empty result list, there would be no command link/button in the view at all during the apply request values phase of the form submit.
Your #{searchManager} bean seems to be request scoped. Request scoped beans have a lifetime of exactly one request-response cycle. So when you submit the form, you'll get a brand new and another instance of the request scoped bean than it was when the form was displayed. The results property seems not to be preserved during (post)construction of the bean and thus remains empty. So JSF cannot find the command link/button in question and thus cannot find the action expression associated with it and thus cannot invoke it.
As you're using JSF2, an easy fix is to place the bean in the view scope. This way the bean will live as long as you're submitting and navigating to exactly the same view by returning null or void in action methods.
#ManagedBean
#ViewScoped
public class SearchManager {
// ...
}
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
I've got a dropdown on my index.xhtml JSF front page. The associated code/commandButton looks like this:
<h:selectOneMenu id="nodes" value="#{MyBacking.chosenNode}">
<f:selectItems value="#{MyBacking.nodes}" />
</h:selectOneMenu>
<a4j:commandButton value="Retrieve" styleClass="ctrlBtn"
id="retrieveBtn" style="margin-bottom: 2px;"
onclick="#{rich:component('nodeLoadWait')}.show()" # modal
action="#{MyBacking.redirect}"
image="/img/btnRetrieve26.png" />
action was set to 'hello' previously, and in my faces-context.xml:
<navigation-rule>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-outcome>hello</from-outcome>
<to-view-id>/nodeMgmt.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
When action was set to 'hello', clicking the retrieve button worked OK in that faces would handle the nav and MyBacking.setChosenNode method would assign all the necessary data, so that the content of nodeMgmt.xhtml would be displayed fully populated.
However, if the initial activity caused by the user clicking retrieve times out, the web page would hang even though the bean detects the time out, and I'd like to redirect the user to a 'timed out' page.
In order to handle the backing bean returning a timedout message (the error detection for which is already present when 'inside' the app), I thought rather than using the faces-context.xml file, I would handle it internally.
I found FacesContext.getCurrentInstance().getExternalContext().redirect but the JSF 1.2 javadoc does not feature this. Perhaps it's because it's not featured? It redirects though which is confusing. Why no documentation on this method?
Nonetheless, it redirects me to the page, but renders without taking into account the data instantiated by the bean during the initial request. The bean is in request scope currently. The relevant code in the bean is
try {
FacesContext.getCurrentInstance().getExternalContext().redirect("nodeMgmt.jsf");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Is using a backend java call the best way to do this kind of redirection?
If not, is it best to use faces-context.xml? If so, how?
While we're here - can anyone direct me to a good reading resource for FacesContext.getCurrentInstance().getExternalContext() usage which has decent examples about how to do simple navigation with data cos I'm having trouble locating one.
Cheers
I found FacesContext.getCurrentInstance().getExternalContext().redirect but the JSF 1.2 javadoc does not feature this. Perhaps it's because it's not featured? It redirects though which is confusing. Why no documentation on this method?
There certainly is.
JSF 1.1: ExternalContext#redirect()
Java EE 5 (JSF 1.2): ExternalContext#redirect()
Java EE 6 (JSF 2.0): ExternalContext#redirect()
Probably you was reading the wrong javadoc. The one of FacesContext perhaps?
Nonetheless, it redirects me to the page, but renders without taking into account the data instantiated by the bean during the initial request. The bean is in request scope currently.
A redirect instructs the browser to fire a brand new HTTP request. So all request scoped beans from the old request will be garbaged and reinitialized in the new request. If you'd like to retain the request, you'd like to use a forward instead (which JSF by default uses), but this isn't going to work on ajax-initiated requests as it will stick to the same page anyway. Only a response with a redirect will force Ajax to change the window location using JavaScript.
If you want to retain some parameters in the new request, you'd have to pass them as request parameters. E.g.
externalContext.redirect("nodeMgmt.jsf?foo=bar");
and set them as managed property in the bean.
I have a problem with finding component in JSF tree. Suppose I have the following template:
<a4j:form id="someForm">
<a4j:outputPanel id="somePanel">
<a4j:repeat id="people" value="#{bean.people}" rowKeyVar="_row" var="_data" stateVar="_state">
<s:decorate id="personPanel" template="edit.xhtml">
<h:outputLabel for="personAge" value="Choose your age:" />
<h:selectOneMenu id="personAge" value="#{_data.age}">
<s:selectItems var="_item" value="#{ageValues}" label="#{_item.description}" />
</h:selectOneMenu>
</s:decorate>
</a4j:repeat>
</a4j:outputPanel>
</a4j:form>
Namespaces are defined as:
xmlns:a4j="http://richfaces.org/a4j"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:s="http://jboss.com/products/seam/taglib"
As you can see, there is a a4j:repeat tag, so there can be n rendered select inputs on the page. How can I find n-th component in JSF tree at the server side? At the client side, components are rendered like: someForm:somePanel:0:personPanel:personAge. I'm trying to find component this way:
UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
UIInput ageInput = (UIInput) root.findComponent("someForm:somePanel:0:personPanel:personAge");
But it couldn't be find. I've checked tree, and it seems like component with that id doesn't exist.
So how can I obtain this component? Is there any way to achieve that?
EDIT:
I've found some workaround. Actually, I didn't need components, but their values. Values can be retrieved from request by their names. Following code:
FacesContext facesContext = FacesContext.getCurrentInstance();
String ageValue = facesContext.getExternalContext().getRequestParameterMap().get("someForm:somePanel:0:personPanel:personAge");
did the work.
a4j:repeat isn't a tag handler that would create dedicated components for each iteration. Rather, it causes its child components to be visited repeatedly during each phase of the JSF lifecycle. That is, there isn't a dedicated component for each row.
For more information on the difference between tag handlers and components, see:
https://rogerkeays.com/jsf-c-foreach-vs-ui-repeat
It can usually be avoided to lookup components by name on the Java side. If you told us why you're trying to do this, we might suggest alternatives.
Edit: Validation in JSF is usually done by a Validator or (for complex cases) in the action method by working directly on the data in the backing bean, putting FacesMessage into the FacesContext manually. I don't see why you'd need the component for validation?