I hope you can help me.
I work with jsf and richefaces, I have a jsf page and I want to include all other pages according to a parameter and my problem is how can I introduce the test condition in my xhtml file.
THanks for your response,
there is my code but it does not work,
my bean
public class TestBean {
private boolean ajoutH;
private boolean listH;
public boolean isListH() {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String,String> params =
fc.getExternalContext().getRequestParameterMap();
String id = params.get("listH");
if(id.equals("listH")){
return true;}
else{
return false;
}
}
public boolean isAjoutH() {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String,String> params =
fc.getExternalContext().getRequestParameterMap();
String id = params.get("ajoutH");
if(id.equals("ajoutH")){
return true;}
else{
return false;}
And my page jsf
<rich:panelMenuGroup label="Hotes">
<rich:panelMenuItem>
<h:outputText value="Ajouter Hote" event="onclick">
<f:param value="{ajoutH}" name="ajoutH" />
</h:outputText>
</rich:panelMenuItem>
<rich:panelMenuItem >
<h:outputText value="Gérer Hotes" >
<f:param value="{listH}" name="listH" />
</h:outputText>
......
<h:panelGroup rendered="#{TestBean.ajoutH}">
<ui:include src="./ajoutHote.xhtml"/>
</h:panelGroup>
<h:panelGroup rendered="#{TestBean.listH}">
<ui:include src="./listHotes.xhtml"/>
</h:panelGroup>
There are different options here and since you're using richfaces I'll include an example for that one too.
If you go for a pure JSF approach, use an <h:panelGroup> with a rendered attribute on it, defined by your bean. This attribute must be a boolean.
<h:panelGroup rendered="#{yourBean.yourCondition}">
<ui:include src="pages/yourPage.xhtml">
...
</h:panelGroup>
Another option is to use an <a4j:region>. It works in the same way basically.
<a4j:region rendered="#{yourBean.yourCondition}">
<ui:include src="pages/yourPage.xhtml">
...
</a4j:region>
Regardless of your choice, your bean should define the condition:
public Boolean isYourCondition() {
return /*your logic here*/;
}
There are many approaches to take, just take your pick.
use a4j:outputpanel and inside it ui:include:
<a4j:outputPanel rendered="#{someBean.someBoolean}">
<ui:include src="somepage.xhtml" />
</a4j:outputPanel>
Related
I just started learning jsf and I encountered a basic problem. I displayed a list of objects in dataTable and created form for each row to call a function with right parameter:
<h:dataTable value="#{test.getMyItems()}" var="o"
styleClass="order-table"
headerClass="order-table-header"
rowClasses="order-table-odd-row,order-table-even-row">
<h:column>
<f:facet name="header">Content</f:facet>
#{o.content}
</h:column>
<h:column>
<h:form>
<h:commandLink action="#{test.edit}" value="edit">
<f:param name="id" value="#{o.id}" />
</h:commandLink>
</h:form>
</h:column>
</h:dataTable>
And now I wanted to redirect user to edit page whit a form which has inputs set to proper values. So in function edit I set values to the variables:
public String edit() {
this.name = "test1";
this.description = "test2";
return "/edit.xhtml?faces-redirect=true";
}
And in edit view I wanted to display them:
<h:form>
<h:outputLabel value="name:" />
<h:inputText value="#{test.name}" />
<h:outputLabel value="description" />
<h:inputText value="#{test.description}" />
<h:commandButton value="Add" action="#{edit.update()}"/>
</h:form>
but inputs are empty. Why?
And Test class:
#ManagedBean (name = "test")
#ViewScoped
public class Test {
private String name;
private String description;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String edit() {
this.name = "test1";
this.description = "test2";
return "/edit.xhtml?faces-redirect=true";
}
public List<Item> getMyItems() {
List<Item> items = new ArrayList<Item>();
Item i = new Item("1", "g", "f");
items.add(i);
return items;
}
public void update() {
}
They are empty because you're sending a redirect to a new view. A view scoped bean lives as long as the current view lives. A redirect to a new view destroys this.
In this specific construct, you need a <h:link>, not a <h:commandLink>.
Replace
<h:form>
<h:commandLink action="#{test.edit}" value="edit">
<f:param name="id" value="#{o.id}" />
</h:commandLink>
</h:form>
by
<h:link value="edit" outcome="edit">
<f:param name="id" value="#{o.id}" />
</h:link>
with in edit.xhtml
<f:metadata>
<f:viewParam name="id" value="#{edit.id}" />
<f:viewAction action="#{edit.init}" />
</f:metadata>
See also:
Creating master-detail pages for entities, how to link them and which bean scope to choose
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
How to navigate in JSF? How to make URL reflect current page (and not previous one)
How to choose the right bean scope?
I have a page with an inputtextwhere I type the name of an author to search for, when I click the a4j:commandButton searchButton, I'd like to enable the h:commandButton GoToBUtton, if the search returns no authors and to disabled that button, if the search returns an author. At first, the GoToButton should be disabled, that's the default behaviour.
The problem is, goToButton is always disabled, it never changes. What's wrong?
<h:form>
<div align="left">
<rich:panel id="panel" style="width:310px">
<h:panelGrid columns="3">
Nome do autor: <h:inputText id="nameInput"
value="#{insertAuthControllerPt1.nameToSearch}"/>
<a4j:commandButton id="searchButton" value="Procurar"
action="#{insertAuthControllerPt1.searchAuthor()}"
render="authorTable, goToButton">
</a4j:commandButton>
</h:panelGrid>
</rich:panel>
<h:panelGrid id="authorTable">
<rich:dataTable value="#{insertAuthControllerPt1.authorListOfMap}" var="result">
<c:forEach items="#{insertAuthControllerPt1.variableNames}" var="vname">
<rich:column>
<f:facet name="header">#{vname}</f:facet>
#{result[vname]}
</rich:column>
</c:forEach>
</rich:dataTable>
<br />
</h:panelGrid>
<h:commandButton id="goToButton" value="Go" action="InsertAuthorPt2"
disabled="#{insertAuthControllerPt1.disabled}">
<f:setPropertyActionListener target="#{insertAuthorController.flag}"
value="true" />
</h:commandButton>
</div>
</h:form>
Search Method at InsertAuthControllerPt1.java
public class InsertAuthorControllerPt1 {
private boolean disabled;
private String nameToSearch;
private List<String> variableNames;
private List<Map<String, String>> authorListOfMap;
private String flag;
private Map<String, Object> sessionMap;
private List<Author> authors;
public InsertAuthorControllerPt1() {
this.authorListOfMap = new ArrayList<Map<String, String>>();
this.variableNames = new ArrayList<String>();
this.sessionMap = new HashMap<String, Object>();
this.authors = new ArrayList<Author>();
this.disabled = true;
}
public void searchAuthor() {
this.variableNames.clear();
this.authorListOfMap.clear();
if( this.nameToSearch.equals("") ) {
this.disabled = true;
} else {
try {
this.findAuthorByNameOperation(this.sessionMap, this.flag, this.nameToSearch);
}catch(NullPointerException n) {
this.disabled = false;
}catch (Exception e) {
e.printStackTrace();
}
if( (authors != null) && (!authors.isEmpty()) ) {
this.disabled = true;
for( Author author : authors ) {
Map<String, String> map = new HashMap<String, String>();
map.put("URI", author.getUri().toString());
map.put("Nome", author.getName());
map.put("Email", author.getEmail());
this.authorListOfMap.add(map);
}
this.addVariableNames();
}
}
First it is highly discouraged to mix JSTL tags and Facelets tags (see: JSTL in JSF2 Facelets… makes sense?)
Then you will use the f:ajax tag. You will basically use an ajax call to render the goToButton after the action has been executed
<h:commandButton id="searchButton" value="Search" action="#{insertAuthControllerPt1.searchAuthor()}">
<f:ajax render="goToButton">
</h:commandButton>
NOT to miss about using AJAX with JSF:
Java tutorial : Sending an Ajax Request
Max Katz : Learning JSF2: Ajax in JSF – using f:ajax tag
Lincoln Baxter : render components outside of the form
change h:commandButton to a4j:commandButton and add render attribute:
<a4j:commandButton id="searchButton" value="Search"
action="#{insertAuthControllerPt1.searchAuthor()}"
render="goToButton" />
I want to implement a filtering facility in a JSF web application as follows: The users can add as many filters as they want. They can also delete them. So I am having a dataTable of filters. Each row consists of one h:selectOneMenu which has an ajax “change” event in order to make a second h:selectOneMenu visible in the same row. The options of the second h:selectOneMenu are calculated dynamically according to the selected option of the first.
The problem is that the value of second h:selectOneMenu is never set to the back-end object even if I added an ajax event. However the value of the first h:selectOneMenu is set.
I have the following fragment of code in an .xhtml page:
<h:form id="filterForm">
<h:dataTable id="filterTable" value="#{filterManager.filters}" var="filter">
<h:column>
<h:outputLabel value="#{msgs.filterBy}:" for="availableFilters" />
<h:selectOneMenu id="availableFilters" value="#{filter.filter}">
<f:selectItems value="#{filterManager.getProperties(typeSelector.typeSelected)}" />
<f:ajax event="change" render=":filterForm" />
</h:selectOneMenu>
</h:column>
<h:column>
<h:panelGroup id="filterValuesPanel" >
<h:outputLabel value="#{msgs.value}:" for="filterValues" rendered="#{!filter.filterEmpty}" />
<h:selectOneMenu value="#{filter.value}" id="filterValues" rendered="#{!filter.filterEmpty}" >
<f:selectItems value="#{filterManager.getPossibleAnswers(filter)}" />
<f:ajax event="change" render=":filterForm" />
</h:selectOneMenu>
</h:panelGroup>
</h:column>
<h:column>
<h:commandButton value="#{msgs.delete}" title="#{msgs.deleteFilter}">
<f:ajax event="click" listener="#{filterManager.removeFilter(filter)}" render=":filterForm" />
</h:commandButton>
</h:column>
</h:dataTable>
<h:commandButton value="#{msgs.addNewFilter}">
<f:ajax event="click" listener="#{filterManager.addNewFilter}" render=":filterForm" />
</h:commandButton>
</h:form>
I have a bean called “FilterManager” which has a ViewScoped. Important parts are shown below:
#ManagedBean
#ViewScoped
public class FilterManager implements Serializable {
private List<Filter> filters; // it has a getter
private int currentFilterId;
public void addNewFilter(AjaxBehaviorEvent event) {
this.currentFilterId++;
this.filters.add(Filter.getEmptyFilter(this.currentFilterId));
}
public void removeFilter(Filter filter) {
this.filters.remove(filter);
}
...
}
The Filter class is a normal class (not a bean) and is shown below:
public class Filter implements Serializable {
private int id;
private String filter;
private String value;
public String getFilter() {
return filter;
}
public void setFilter(String theFilter) {
if (theFilter != null && !theFilter.isEmpty())
this.filter = theFilter;
}
public String getValue() {
return value;
}
public void setValue(String theValue) {
this.value = theValue;
}
public boolean isFilterEmpty() {
return this.filter == null || this.filter.isEmpty();
}
...
}
Notice that TypeSelector is a SessionScoped bean which has a typeSelected property along with getter and setter.
The problem is: filter.filter is set correctly whereas filter.value is never set. I can't find the problem so I need your help please. Apologies for all this code but I needed to provide you with all the necessary details.
Thanks in advance!
Okay guys that was my fault. I had a bug in FilterManager.getPossibleAnswers(Filter filter) method. Basically, at the end of the method, I was setting filter.value to the first element of List unconditionally. Eg instead of writing
if (filter.getValue() == null || filter.getValue().isEmpty()) {
SelectItem first = answers.get(0);
filter.setValue((String) first.getValue());
}
I just wrote:
SelectItem first = answers.get(0);
filter.setValue((String) first.getValue());
Although filter.value was updating as normal, the value was changing back to default (first element in list) during re-rendering of dataTable component.
i have the following bean:
public class MyBean {
public ArrayList<ReportRow> getReportRows()
{
return reportRows;
}
private final ArrayList<ReportRow> reportRows =
new ArrayList<ReportRow>(Arrays.asList(
new ReportRow("","")
));
public ArrayList<ReportRow> getOrderList() {
return reportRows;
}
public String addAction() {
ReportRow row = new ReportRow("", "");
reportRows.add(row);
return null;
}
public class ReportRow{
String reportColumnName;
String reportColumnDesc;
public ReportRow(String reportColumnName,String reportColumnDesc) {
this.reportColumnName=reportColumnName;
this.reportColumnDesc=reportColumnDesc;
}
public String getReportColumnName()
{
return reportColumnName;
}
public void setReportColumnName(String reportColumnName)
{
this.reportColumnName = reportColumnName;
}
public String getReportColumnDesc()
{
return reportColumnDesc;
}
public void setReportColumnDesc(String reportColumnDesc)
{
this.reportColumnDesc = reportColumnDesc;
}
}
}
jsf page:
<t:dataTable value="#{myBean.reportRows}" var="o"
id="reportColumnsTable" styleClass="standardTable" headerClass="standardTable_Header"
rowStyleClass="#{myBean.viewDelayedRsd}"
>
<h:column>
<t:outputLabel value="Column name:"></t:outputLabel>
<t:inputText id="ReportColumnName" value="#{o.reportColumnName}" required="true">
</t:inputText>
</h:column>
<h:column>
<t:outputLabel value="Column Desc:"></t:outputLabel>
<t:inputText id="ReportColumnDesc" value="#{o.reportColumnDesc}" >
</t:inputText>
</h:column>
<h:column>
<h:outputLink value="#add"><h:outputText value="Add"/>
<a4j:support ajaxSingle="true" event="onclick" action="#{rprtBean.addAction}"
reRender="reportColumnsTable,msgPanel" />
</h:outputLink>
</h:column>
</t:dataTable>
problem is that when i click on add, it generates a new row, and clear the old one, and i want to maintain the values of old row, any ideas ?
You're using a <h:outputLink> instead of a <h:commandLink>. The <h:outputLink> doesn't submit the form at all, it fires a plain GET request. The <a4j:support> won't work properly inside a <h:outputLink>. Replace it by a <h:commandLink>:
<h:commandLink value="Add" action="#{rprtBean.addAction}">
<a4j:support reRender="reportColumnsTable,msgPanel" ajaxSingle="true" />
</h:commandLink>
Then, you need to ensure that you preserve the data model for subsequent request in case that your bean is request scoped. There are several ways to achieve this:
Set either Tomahawk datatable's preserveDataModel to true:
<t:dataTable preserveDataModel="true">
Or save the bean state in the view scope. Add the following tag somewhere in the page:
<t:saveState value="#{myBean}" />
or since you're also using RichFaces/Ajax4jsf:
<a4j:keepAlive beanName="myBean" />
i just used the a4j command link and everything worked fine.
I've been using JSF for a while but there's something that has always confused me. Hopefully someone can help.
Simple example, there's a page that shows a table of "Person"s and when you click on the "Person" name, it takes you to a page to view the details of the "Person".
Typically, I implement a personSearch.jsf page like this:
<h:dataTable value="#{personHandler.persons}" var="person">
<h:column>
<h:commandLink action="#{personHandler.show( person.id )}" >
<h:outputText value="#{person.name}" />
</h:commandLink>
</h:column>
</h:dataTable>
And I implement a personView.jsf page like this:
<h:panelGrid columns="2">
<h:outputText value="Person ID:" />
<h:outputText value="#{personHandler.selectedPerson.id}" />
<h:outputText value="Person Name:" />
<h:outputText value="#{personHandler.selectedPerson.name}" />
</h:panelGrid>
PersonHandler.show(Integer personId) sets personHandler.selectedPerson and then redirects to the personView page.
This all works fine when PersonHandler is a session bean. But I prefer it to be a request scoped bean because the user may have several windows open and I don't want there to be only one selected person per session.
So my question is, what's the "correct" way to do this JSF? I was once able to get what I wanted using a4j:keepAlive on the personHandler, but that always felt like a kludge. Again, this is something I've never understood about JSF.
Any help is greatly appreciated!
rob
If the view is supposed to be bookmarkable, pass the person ID as a GET request parameter instead of a POST request "parameter".
<h:outputLink value="viewperson.xhtml">
<f:param name="id" value="#{person.id}" />
</h:outputLink>
This way you can use two #RequestScoped beans, one for the list and one for the view. You can preload the selected person as follows:
#ManagedProperty(value="#{param.id}")
private Long id;
#PostConstruct
public void init() {
selectedPerson = personDAO.find(id);
}
If it is not supposed to be bookmarkable, then just create a single view which renders the view state conditionally.
<ui:fragment rendered="#{!personHandler.viewMode}">
<h:form>
<h:dataTable value="#{personHandler.persons}" var="person">
<h:column>
<h:commandLink value="#{person.name}" action="#{personHandler.show(person)}" />
</h:column>
</h:dataTable>
</h:form>
</ui:fragment>
<ui:fragment rendered="#{personHandler.viewMode}">
<h:form>
...
<h:commandLink value="Go back" action="#{personHandler.back}" />
</h:form>
</ui:fragment>
(You can if necessary split out the content of the both framgents to another Facelet files which you include by <ui:include>)
This way you can use a single #ViewScoped bean with action methods returning void or null.
public void show(Person selectedPerson) {
this.selectedPerson = selectedPerson;
}
public void back() {
selectedPerson = null;
}
public boolean isViewMode() {
return selectedPerson != null;
}
You can even wrap the whole view in some
<h:panelGroup id="container">
and nest the following in both command links to let Ajax magic do the work
<f:ajax execute="#form" render=":container" />