h:commandButton inside h:dataTable [duplicate] - jsf

This question already has answers here:
How can I pass selected row to commandLink inside dataTable or ui:repeat?
(4 answers)
Closed 7 years ago.
I am using a JSF data table. One of the columns in the table is a Command button.
When this button is clicked I need to pass few parameters (like a value of the selected row) using the Expression language. This paramaters need to be passed to the JSF managed bean which can execute methods on them.
I have used the following snippet of code but the value i am getting on the JSF bean is always null.
<h:column>
<f:facet name="header">
<h:outputText value="Follow"/>
</f:facet>
<h:commandButton id="FollwDoc" action="#{usermanager.followDoctor}" value="Follow" />
<h:inputHidden id="id1" value="#{doc.doctorid}" />
</h:column>
Bean Method:
public void followDoctor() {
FacesContext context = FacesContext.getCurrentInstance();
Map requestMap = context.getExternalContext().getRequestParameterMap();
String value = (String)requestMap.get("id1");
System.out.println("Doctor Added to patient List"+ value);
}
How can I pass values to the JSF managed bean with a commandbutton?

Use DataModel#getRowData() to obtain the current row in action method.
#ManagedBean
#ViewScoped
public class Usermanager {
private List<Doctor> doctors;
private DataModel<Doctor> doctorModel;
#PostConstruct
public void init() {
doctors = getItSomehow();
doctorModel = new ListDataModel<Doctor>(doctors);
}
public void followDoctor() {
Doctor selectedDoctor = doctorModel.getRowData();
// ...
}
// ...
}
Use it in the datatable instead.
<h:dataTable value="#{usermanager.doctorModel}" var="doc">
And get rid of that h:inputHidden next to the h:commandButton in the view.
An -less elegant- alternative is to use f:setPropertyActionListener.
public class Usermanager {
private Long doctorId;
public void followDoctor() {
Doctor selectedDoctor = getItSomehowBy(doctorId);
// ...
}
// ...
}
With the following button:
<h:commandButton action="#{usermanager.followDoctor}" value="Follow">
<f:setPropertyActionListener target="#{usermanager.doctorId}" value="#{doc.doctorId}" />
</h:commandButton>
Related:
The benefits and pitfalls of #ViewScoped - Contains CRUD example using DataModel<E>.

Related

I can't pass the param after clicking the h:commandLink

I'm trying to create a quizz with data from a db but I'm a bit lost.
I have 3 tables :
Quizz
Questions (ManyToOne: Quizz)
Answers (ManyToOne: Questions)
So I wrote this code to display each question with its answers:
<p>Questions</p>
<ui:repeat value="#{questionBean.fetchQuestionsFromQuizz(quizzBean.fetchQuizz(quizzBean.quizzId))}" var="i" varStatus="current">
<p>#{i.question}</p>
<h:selectOneRadio value="#{i.selectedAnswerId}">
<f:selectItems value="#{answerBean.fetchAnswersFromQuestion(i)}" var="item"
itemLabel="#{item.answer}" itemValue="#{item.id}" />
</h:selectOneRadio>
</ui:repeat>
I'm getting the "quizzBean.quizzId" through a GET param.
The value of h:selectOneRadio is the selectedAnswerId property in my Question entity, so I'm trying to bind it with the selected answer by the user.
So I'm trying to pass the quizz id in the submit button to check them in the bean(#RequestScoped), but the quizz id is null once clicking on the submit button...
Here's the button:
<h:commandLink value="Submit" action="#{questionBean.checkTheAnswers()}">
<f:setPropertyActionListener target="#{questionBean.fromQuizzId}" value="#{quizzBean.quizzId}" />
</h:commandLink>
And here's the method (a simple print):
public void checkTheAnswers(){
System.out.println("FROMQUIZZID : " + fromQuizzId);
}
So do you have any idea on why the quizzId is null?
You have a requestScoped managed bean and you pass the quizzId using the http GET method, in this case you shoud initialize the quizzId filed with the value from the GET method using the #PostConstruct init method of the managedbean.
#PostConstruct
public void init() {
try {
FacesContext facesContext = FacesContext.getCurrentInstance();
Map<String, String> params = facesContext.getExternalContext().getRequestParameterMap();
if (params.containsKey("quizzId") {
quizzId= params.get("quizzId");
....

Passing Values from page to other page JSF

I am beginner in java server faces (JSF), I need to pass the content of text input to second page to display it, the same applies for the second page: I want to pass radio buttons values to a third page. I searched and tried a lot without success.
For example I tried
<h:commandButton value="Next" action="#{myBean.execute(input_id.value)}"/>
Execute method is:
public void execute(String value) {
// ...
try{
FacesContext.getCurrentInstance().getExternalContext().dispatch("/Quizy.xhtml?faces-redirect=true");
}
catch(Exception e){
System.out.println("err");
}
}
Any suggestions?
Here are 4 other ways to pass a parameter value from JSF page to other page JSF :
1- Method expression (JSF 2.0)
2- f:param
3- f:attribute
4- f:setPropertyActionListener
1. Method expression
Since JSF 2.0, you are allow to pass parameter value in the method expression like this #{bean.method(param)}.
JSF page
<h:commandButton action="#{user.editAction(delete)}" />
ManagedBean
#ManagedBean(name="user")
#SessionScoped
public class UserBean{
public String editAction(String id) {
//id = "delete"
}
}
2- f:param
Pass parameter value via f:param tag and get it back via request parameter in backing bean.
JSF page
<h:commandButton action="#{user.editAction}">
<f:param name="action" value="delete" />
</h:commandButton>
ManagedBean
#ManagedBean(name="user")
#SessionScoped
public class UserBean{
public String editAction() {
Map<String,String> params =
FacesContext.getExternalContext().getRequestParameterMap();
String action = params.get("action");
//...
}
}
3. f:atribute
Pass parameter value via f:atribute tag and get it back via action listener in backing bean.
JSF page
<h:commandButton action="#{user.editAction}" actionListener="#{user.attrListener}">
<f:attribute name="action" value="delete" />
</h:commandButton>
ManagedBean
#ManagedBean(name="user")
#SessionScoped
public class UserBean{
String action;
//action listener event
public void attrListener(ActionEvent event){
action = (String)event.getComponent().getAttributes().get("action");
}
public String editAction() {
//...
}
}
4. f:setPropertyActionListener
Pass parameter value via f:setPropertyActionListener tag, it will set the value directly into your backing bean property.
JSF page
<h:commandButton action="#{user.editAction}" >
<f:setPropertyActionListener target="#{user.action}" value="delete" />
</h:commandButton>
ManagedBean
#ManagedBean(name="user")
#SessionScoped
public class UserBean{
public String action;
public void setAction(String action) {
this.action = action;
}
public String editAction() {
//now action property contains "delete"
}
}
There are several ways for doing this, but here is one of them.
You will need to save the inputText value into a property of your bean and both your h:inputText and your h:commanButton should be in the same h:form element
Here is a sample code
In your view
<h:form>
...
<h:inputText value={myBean.someValue} />
....
<h:commandButton value="Next" action="#{myBean.execute()}"/>
</h:form>
Your managed bean should be at least session scoped if you want your property (someValue) to be available in different pages. The content of the managed bean should look like this also:
private String someValue;
// Getter and setter for `someValue`
public String execute() {
// ...
return "/Quizy.xhtml?faces-redirect=true";
}
In the second page if you want to retrieve that value, just use #{myBean.someValue}
to have this done, you just need to set the Value of Your component here inputText or radioButton to a Property of your Managed bean or Cdi bean called on the page of course you won't forget to have getter and setter method for ur property in ur bean. Finally be sure that the scope of Ur bean allow it to be alive (with all its properties' value) across the session. Then, from ur end page you may call ur Managed bean or Cdi bean proprety as value of page components

Auto-updating filter values on DataTable

I'm currently using filters with options list on my lazy loaded DataTables. Everything works just fine except I'd like to be able to reload my filter options depending on currently selected filters.
For example, my DataTable has two fields country and region, and I want to filter
DataTable snippet :
<p:dataTable var="d" widgetVar="personneContactTable" value="#{bean.dataModel}" id="myDataTable" lazy="true">
<p:column sortBy="country" filterBy="country" filterOptions="#{bean.getCountryOptions()}">
[...]
</p:column>
<p:column sortBy="region" filterBy="region" filterOptions="#{bean.getRegionOptions()}">
[...]
</p:column>
</p:dataTable>
And my bean :
#ManagedBean(name = "bean")
#SessionScoped
public class MyBean implements Serializable {
LazyDataModel<MyStuff> dataModel;
String country;
String region;
public SelectItem[] getCountryOptions() {
return service.someMagic();
}
public SelectItem[] getRegionOptions() {
return service.someMoreMagic(country);
}
// + getters, setters, etc.
}
I tried using properties instead of methods, adding <p:ajax event="filter"> tags to try reloading part of the DataTable, but nothing worked. I found http://forum.primefaces.org/viewtopic.php?f=3&t=38087 too, but again not workable solution emerged.
How can I automaticaly refresh my filterOptions? (this is using Primefaces 4 ELITE branch)

Best solution to pass objects between two ViewScoped ManagedBeans

I'm wondering what the best practices are to pass data (an object) between two ViewScoped beans.
They need to be view scoped because of the problem that's brilliantly explained here (to put it short: In both views I'm using a h:commandLink from within a h:dataTable which requires the data model to still be present when submitting).
My problem now is that clicking the link also navigates to a new view, so using the following code, my object gets passed but the DetailViewController instance gets killed right after that and a new one is created when the view changes (as you would expect).
View:
<h:dataTable value="#{searchController.dataModel}" var="item">
...
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink id="open" value="open" action="#{searchController.showDetail(item)}" />
</h:column>
</h:dataTable>
Bean:
#ManagedBean
#ViewScoped
public class SearchController {
#ManagedProperty(value="#{detailViewController}")
private DetailViewController detailViewController;
// getters, setters, etc. ...
public String showDetail(Item i) {
detailViewController.setItem(i);
return "view_detail.xhtml";
}
}
How would you solve this? I thought about putting the object inside Flash: FacesContext.getExternalContext.getFlash()... Is there an easier or more elegant solution?
You can use view parameters. (See How do you pass view parameters when navigating from an action in JSF2?)
Typically, your method return the url with query parameters:
public String showDetail(Item i) {
return "view_detail.xhtml?id="+i.getId();
}
And in your view_detail.xhtml file, you add a f:viewParam tag evaluating to on of your bean field:
<f:metadata>
<f:viewParam name="id" value="#{myBean.id}" />
</f:metadata>
Then from your backing bean, you use that field to get your Item instance in your #postConstruct method.
If you don't use the f:viewparam tag, you can also fetch the request parameters to obtain the id.
private String id;
private Item item;
#PostConstruct
public void init() {
if (id != null) {
item = fetchItem(id);
} else {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
Map<String, String> requestParameterMap = externalContext.getRequestParameterMap();
if (requestParameters.containsKey("id")) {
id = requestParameters.get("id");
item = fetchItem(id);
} else {
throw new WebServiceException("No item id in request parameters");
}
}
}

Values of h:inputText inside ui:repeat are not processed

I want to process this form (valueChangueListener is not valid in real case).
This is the back bean:
public class TestBean extends PrivateBaseBean implements Serializable {
private List<String> strings;
#PostConstruct
public void init() {
strings = new ArrayList<String>();
strings.add("");
strings.add("");
strings.add("");
}
public void saveAction(ActionEvent event) {
StringBuilder textToShowInMessage = new StringBuilder();
for (String string : strings) {
textToShowInMessage.append(string);
textToShowInMessage.append("; ");
}
FacesMessage msg = new FacesMessage(super.getBundle().getString(
textToShowInMessage.toString()), "");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
getters... setters...
An the view:
....
<h:form>
<ui:repeat var="string" value="#{testBean.strings}">
<h:inputText value="#{string}" />
<br />
</ui:repeat>
<p:commandButton value="#{msg.save}"
actionListener="#{testBean.saveAction}" icon="ui-icon-disk"
update="#form" />
</h:form>
...
When the form is processed in the back bean string list always is blank.
How to process form intput's inside iteration, without any value changue listener?
There are some screenshots:
The same problem occurs with action or actionListener on
Your problem is not connected with PrimeFaces <p:commandButton>'s behaviour, but rather with a scoping problem that is implicilty created when using the <ui:repeat> tag.
First of all, let's depart from your example. Basically, you've got
<ui:repeat value="#{bean.strings}" var="s">
<h:inputText value="#{s}"/>
</ui:repeat>
with the backing List<String> strings.
The culprit is here: value="#{s}". The exported by <ui:repeat> variable s is visible only within its loop and it is not bound to any managed bean's property, but instead only to a local variable. Put it differently, s is not bound/equal to bean.strings[index] as one would expect and has no knowledge, as we see, where it originated from. So basically, you're off with a unilateral relationship: value from the bean is printed in your input properly, but the reverse is not happening.
The workarounds
Workaround #1: wrapper classes / model objects
The situation can be overcome by using a wrapper object for your class. In case of a string it could be a 'simple mutable string', like below:
public class MString {
private String string;//getter+setter+constructor
}
In this case the iteration will be working as predicted:
<ui:repeat value="#{bean.mstrings}" var="ms">
<h:inputText value="#{ms.string}"/>
</ui:repeat>
with the backing List<MString> mstrings.
Note that if you have your model class, like User, and will change its properties within <ui:repeat> the class itself will be effectively a wrapper, so that the properties will be set appropriately.
Workaround #2: chained property access
Another workaround consists of accessing an element of your collection directly from within a <h:inputText> tag. This way, any such property will be set by accessing the bean, then collection, then setting the property at the desired index. Excessively long, but that's how it is. As to the how question, <ui:repeat> provides for an exported current iteration status variable, varStatus, that will be used to access the array/collection in the managed bean.
In this case the iteration will also be working as predicted:
<ui:repeat value="#{bean.strings}" var="s" varStatus="status">
<h:inputText value="#{bean.strings[status.index]}"/>
</ui:repeat>
with the ordinary backing List<String> strings.
My workaround solution take the value directly from the page:
<ui:repeat id="repeat" value="#{bean.strings}" var="s" varStatus="status">
<h:inputText id="x" value="#{s.field}"/>
<h:commandLink style="margin: .5em 0" styleClass="commandLink" actionListener="#{bean.save(status.index)}" value="#{bundle.Send}"/>
</ui:repeat>
The save method:
public void save(String rowid) {
String jsParam = Util.getJsParam("repeat:" + rowid + ":x");
System.out.println("jsParam: " + jsParam); //persist...
}
The getJsParam method:
public static String getJsParam(String paramName) {
javax.faces.context.FacesContext jsf = javax.faces.context.FacesContext.getCurrentInstance();
Map<String, String> requestParameterMap = jsf.getExternalContext().getRequestParameterMap();
String paramValue = requestParameterMap.get(paramName);
if (paramValue != null) {
paramValue = paramValue.trim();
if (paramValue.length() == 0) {
paramValue = null;
}
}
return paramValue;
}

Resources