I have a simple input form that accepts data from the user and stores the data in DB upon a click action for a p:commandButton
the form looks like this
form.xhtml :
<div align="center" style="width: 80%">
<p:panelGrid columns="1">
<p:accordionPanel>
<p:tab title="#{msg['truck.tab.general']}">
<h:form><h:panelGrid columns="3">
<p:outputLabel value="*#{msg['truck.form.code']}"></p:outputLabel>
<p:inputText id="inputTruckCode" required="true"
requiredMessage="Must exist"
value="#{trucksBean.currentTruck.code}"></p:inputText>
<p:message for="inputTruckCode" showSummary="true"></p:message>
<!-- rest of the form -->
</h:form>
</p:tab>
</p:accordionPanel>
<p:commandButton action="#{trucksBean.insertTruck}"
value="Insert Truck"></p:commandButton>
</p:panelGrid>
</div>
the backing bean code:
#ManagedBean
#ViewScoped
public class TrucksBean {
private TruckFullInformation currentTruck;
public String insertTruck() {
//the values inside currentTruck member variable are NULLs
return null;
}
what's wrong with the code? why clicking on the commandButton doesn't affect/update the values of the "currentTruck" variable?
thanks in advance;
Related
I have a very simple JSF 2/Facelets page that looks like this:
<ui:repeat value="#{myBean.names}" var="_name">
<h:commandLink value="#{_name}" action="#{myBean.sayHello(_name)}">
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</ui:repeat>
The backing bean provides a java.util.List<String> with names and the action-method just prints a "hello <name>" message to standard output.
This works fine. I get a list of names in the browser and a click fires the action-method that says hello to the specified name.
The Problem arises, when I want to put this code in a composite component that does the iteration and renders the actual link via a facet:
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:cc="http://xmlns.jcp.org/jsf/composite">
<cc:interface>
<cc:attribute name="value" type="java.util.List" required="true" />
<cc:facet name="content" />
</cc:interface>
<cc:implementation>
<ui:repeat value="#{cc.attrs.value}" var="_name">
<cc:renderFacet name="content"/>
</ui:repeat>
</cc:implementation>
</ui:component>
I use the composite component like this:
<my:myComp value="#{bean.names}">
<f:facet name="content">
<h:commandLink value="#{_name}" action="#{bean.sayHello(_name)}">
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</f:facet>
</my:myComp>
In the browser I get a list of names that looks exactly like before. But clicking a link now renders a "hello null" message. So _name is resolved correctly in the value attribute of <h:commandLink> but not in the action attribute.
I also tried using actionListener instead of action or the listener attribute from the <f:ajax> tag with no difference.
Could anybody shade some light on this issue?
My environment:
WildFly 8.1 with
JSF 2.2.6 (Mojarra)
The issue has to do with the scope of the variable in this case _name which is evaluated once when the <ui:repeat/> is being processed. In my case, I ran your code and it produced Hello John even though their were other names in my list. To get around this, I introduced a <f:param/> that would contain the value of the _name, and modified your code as follows:
<h:form>
<my:myComp value="#{bean.names}">
<f:facet name="content">
<h:commandLink value="#{_name}" action="#{bean.sayHello()}">
<f:param name="name_" value="#{_name}"/>
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</f:facet>
</my:myComp>
</h:form>
I also modified the sayHello() method as follows for a #RequestScoped bean:
#ManagedProperty(value = "#{facesContext}")
private FacesContext facesContext;
public void setFacesContext(FacesContext facesContext) {
this.facesContext = facesContext;
}
public void sayHello() {
Map<String, String> params = facesContext.getExternalContext()
.getRequestParameterMap();
String name = params.get("name_");
System.out.println("Hello " + name);
}
You could change this to something shorter in a #ViewScoped bean to:
public void sayHello() {
Map<String, String> params = FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap();
String name = params.get("name_");
System.out.println("Hello " + name);
}
The final result is that it prints out the names correctly.
I have this code :
Add.xhtml :
<h:form id="nouv">
<h:panelGrid columns="2">
<h:outputText value="Nom:"></h:outputText>
<p:inputText value="#{ddeBean.nom}"></p:inputText>
<p:commandButton id="save"
value="ajouter"
update=":nouv:btn"
actionListener="#{ddeBean.ajouter}">
</p:commandButton>
<p:outputPanel id="btn">
<h:outputText rendered="#{ddeBean.created}" value="#{ddeBean.message}"/>
<p:commandButton id="btn_cr" value="add" rendered="#{ddeBean.created}"
action="pool.xhtml?faces-redirect=true">
</p:commandButton>
</p:outputPanel>
</h:panelGrid>
</h:form>
ddeBean.java :
#ManagedBean(name = "ddeBean")
#RequestScoped
public class DemandeBean implements Serializable{
ddeDAO dao = new ddeDaoImpl();
private String nom;
public String message = "";
private boolean created = false;
public void test(ActionEvent event){
Demande p = new Demande();
p.setDde(this.nom);
dao.Nouveau_dde(p);
created = true;
this.setMessage("saved!");
}
}
When I click commandButton Ajouter the message an the commandbutton Add will be shown, but commandbutton Add doesn't redirect to pool.xhtml.
When you click on the button Add, the bean is recreated and the value #{ddeBean.created} then reinitialized to false so the button doesn't get rendered before action take place.
To fix that, you need to change the scope of your bean to #ViewScoped.
You should also make sure to replace
public void test(ActionEvent event){
by
public void ajouter(ActionEvent event){
if you want that your button work properly.
Another solution
Since you are using PrimeFaces, you could show your button by JavaScript :
<p:commandButton id="save"
value="ajouter"
oncomplete="panel-create.show();"
actionListener="#{ddeBean.ajouter}">
</p:commandButton>
<p:outputPanel id="btn" style="display: none;" widgetVar="panel-create">
<h:outputText value="#{ddeBean.message}"/>
<p:commandButton id="btn_cr" value="add"
action="pool.xhtml?faces-redirect=true">
</p:commandButton>
</p:outputPanel>
Note the oncomplete attribute on the p:commandButton, the widgetVar attribute on the p:panel and the rendered attributes removed.
Following is my requirement-
Here I have a p:panelGrid which can add & delete the row of table. The grid contains some p:inputText and various other PrimeFaces components along with a p:fileUpload component in each row. The component p:fileUpload is set with mode="advanced" auto="true" attributes, which automatically uploads the file and hide itself after completing the successful upload.
The whole p:panelGrid is in #ViewScoped, hence working fine. I kept p:fileUpload component in #RequestScoped since for each upload request it has to upload the file but after adding new row, the previous state is not persisted anymore. so the p:fileUpload is starting visible in previous rows also. That's what I don't want. Do I need to write any custom scope for it?
Below is the view-|
<h:form>
<p:panel id="agentForm" header="#{msg.AGENTS_INFORMATION}"
style="overflow:auto; margin-bottom: 2px">
<div align="center" style="margin-top: 20px; margin-bottom: 2px">
<ui:repeat value="#{agent.scenarioList}" var="c">
<p:panelGrid>
<p:row>
<p:column>
<p:inputText id="ipaddress" value="#{c.machineIpAddress}"
style="width:90%">
<p:watermark for="ipaddress" value="#{msg.MACHINE_IP_ADDRESS}" />
</p:inputText>
</p:column>
<p:column>
<p:inputText id="username" value="#{c.machineUsername}"
style="width:90%">
<p:watermark for="username" value="#{msg.MACHINE_USERNAME}" />
</p:inputText>
</p:column>
<p:column>
<p:password id="passwd" value="#{c.machinePassword}">
<p:watermark for="passwd" value="#{msg.MACHINE_PASSWORD}" />
</p:password>
</p:column>
<p:column id="fileUpload">
<p:fileUpload rendered="#{!fileUploadController.hidden}"
label="Upload Script" style="font-size: 100% !important;"
showButtons="false"
fileUploadListener="#{fileUploadController.upload}"
mode="advanced" auto="true" sizeLimit="100000"
allowTypes="/(\.|\/)(py|txt)$/"
update="fileUpload, outPanel, :message" />
<p:outputPanel id="outPanel">
<!-- Below outputLabel will be linked to uploaded file, so that User can see the file -->
<p:outputLabel style="cursor: pointer" value="View uploded Script"
label="View Script" rendered="#{fileUploadController.hidden}" />
</p:outputPanel>
</p:column>
<p:column>
<p:inputText id="testname" value="#{c.testName}"
style="width:90%">
<p:watermark for="testname" value="#{msg.TEST_NAME}" />
</p:inputText>
</p:column>
<p:column>
<p:spinner id="threads" value="#{c.threads}" min="1" max="500"
size="8">
<p:tooltip for="threads" value="#{msg.TEST_NAME}"
showEffect="slide" hideEffect="slide" />
</p:spinner>
</p:column>
<p:column>
<p:selectBooleanCheckbox id="chkSelected" value="#{c.selected}">
<p:tooltip for="chkSelected" value="#{msg.CHECKBOX}"
showEffect="slide" hideEffect="slide" />
</p:selectBooleanCheckbox>
</p:column>
</p:row>
</p:panelGrid>
</ui:repeat>
<p:toolbar style="margin-top: 10px;">
<p:toolbarGroup align="right">
<p:commandButton value="#{msg.ADD_IT}"
update=":message, agentForm"
actionListener="#{agent.addComponent()}" />
<p:commandButton value="#{msg.DELETE_IT}"
update=":message, agentForm"
actionListener="#{agent.deleteComponent()}" />
</p:toolbarGroup>
</p:toolbar>
</div>
</p:panel>
</h:form>
My managed bean which is in #ViewScoped look like this-
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.ravij.performance.model.Scenario;
#ManagedBean(name = "agent")
#ViewScoped
public class AgentInfo implements Serializable {
private static final long serialVersionUID = 1L;
List<Scenario> scenarioList;
#PostConstruct
public void initBean() {
this.scenarioList = new ArrayList<Scenario>();
this.scenarioList.add(new Scenario());
}
public void addComponent() {
if (this.scenarioList != null) {
this.scenarioList.add(new Scenario());
} else {
this.initBean();
}
}
public void deleteComponent() {
List<Scenario> itemsToDelete = new ArrayList<Scenario>();
if (this.scenarioList != null) {
for (Scenario b : this.scenarioList) {
if (b.isSelected()) {
itemsToDelete.add(b);
}
}
this.scenarioList.removeAll(itemsToDelete);
}
}
public List<Scenario> getScenarioList() {
return scenarioList;
}
public void setScenarioList(List<Scenario> scenarioList) {
this.scenarioList = scenarioList;
}
}
The Scenario object contains all the information of a row. Below is the code-
package org.ravij.performance.model;
import java.io.Serializable;
public class Scenario implements Serializable {
private String machineIpAddress;
private String machineUsername;
private String machinePassword;
private String uploadedFilePath;
private String testName;
private int threads = 1;
private boolean selected = false;
//Below are the getters and setter w.r.t all the above variables
//I am not putting it, to make the code short
}
The managed bean FileUploadController is in #RequestScoped
You should simply keep your hidden attribute with the other values in your #ViewScoped bean. Your current code has a single hidden attribute shared with all your <p:fileUpload components which is probably not what you want.
The behavior looks good because you are only updating the current fileUpload but according to your code all the others <p:fileUpload component are supposed to be hidden.
You should also put your <h:form into your <ui:repeat so that you can know the current line which is concerned by the file being uploaded by putting something like an index (which you can get from the <ui:repeat using varStatus attribute) or any other identifier to match the current line in an hidden input.
From #{fileUploadController.upload} the easiest manner to get the hidden parameter is to get the response from FacesContext as explained here : How to get parametrs to BackingBean from jsf page in <ui:repeat>
UPDATE
It was a bit harder than expected, the problem is that <p:fileUpload will send everything in the enclosing form (didn't try to play with process attribute) and thus it will be hard to know what row is concerned by the file upload.
Also I didn't knew that you couldn't put <h:form in your <ui:repeat but the behavior of your delete button is blocking as it expects to get everything in one form.
I made a working POC using dialog to put the fileupload outside, here is how :
The trivial Scenario.java :
public class Scenario implements Serializable {
private String machineIpAddress;
private String machineUsername;
private String machinePassword;
private String uploadedFilePath;
private String testName;
private int threads = 1;
private boolean selected = false;
private boolean hidden = false; // This is new
// + Getters/Setters
}
A few changes in the AgentInfo.java :
#ManagedBean(name = "agent")
#ViewScoped
public class AgentInfo implements Serializable {
private List<Scenario> scenarioList;
private Scenario currentScenario; // This is new
// I removed the #PostConstruct which I rarely use
public void addComponent() {
if (this.scenarioList != null) {
this.scenarioList.add(new Scenario());
}
}
public void deleteComponent() {
if (this.scenarioList == null) {
return;
}
List<Scenario> itemsToDelete = new ArrayList<Scenario>();
for (Scenario scenario : this.scenarioList) {
if (scenario.isSelected()) {
itemsToDelete.add(scenario);
}
}
this.scenarioList.removeAll(itemsToDelete);
}
// This is new, it must be called before opening the upload dialog
// in order to keep a pointer on the current scenario you are working on
public void prepareUpload(Scenario scenario) {
this.currentScenario = scenario;
}
// I put the upload method here
public void upload(FileUploadEvent event) {
// Do what you need to do here
this.currentScenario.setHidden(true);
RequestContext.getCurrentInstance().execute("uploadDialogWidget.hide()");
}
public List<Scenario> getScenarioList() {
if (this.scenarioList == null) {
this.scenarioList = new ArrayList<Scenario>();
this.scenarioList.add(new Scenario());
}
return scenarioList;
}
public void setScenarioList(List<Scenario> scenarioList) {
this.scenarioList = scenarioList;
}
public Scenario getCurrentScenario() {
return currentScenario;
}
public void setCurrentScenario(Scenario currentScenario) {
this.currentScenario = currentScenario;
}
}
The most changes are in the view, I put a <h:commandButton to open the dialog in the form. I also added the dialog, and added the redisplay attribute for your password fields (which is necessary to have if you want to keep the value after form submission).
Note that I removed references to a component with message id which was not gave, don't forget to reintroduce it.
the .xhtml :
<h:form id="agentForm">
<p:panel header="#{msg.AGENTS_INFORMATION}"
style="overflow:auto; margin-bottom: 2px">
<div align="center" style="margin-top: 20px; margin-bottom: 2px">
<ui:repeat value="#{agent.scenarioList}" var="c">
<p:panelGrid>
<p:row>
<p:column>
<p:inputText id="ipaddress" value="#{c.machineIpAddress}"
style="width:90%">
<p:watermark for="ipaddress" value="#{msg.MACHINE_IP_ADDRESS}" />
</p:inputText>
</p:column>
<p:column>
<p:inputText id="username" value="#{c.machineUsername}"
style="width:90%">
<p:watermark for="username" value="#{msg.MACHINE_USERNAME}" />
</p:inputText>
</p:column>
<p:column>
<p:password id="passwd" value="#{c.machinePassword}" redisplay="true">
<p:watermark for="passwd" value="#{msg.MACHINE_PASSWORD}" />
</p:password>
</p:column>
<p:column id="fileUpload">
<p:commandButton icon="ui-icon-arrowthick-1-n" value="Upload"
actionListener="#{agent.prepareUpload(c)}"
update=":uploadDialog"
oncomplete="uploadDialogWidget.show()"
rendered="#{!c.hidden}" />
<p:outputPanel id="outPanel">
<!-- Below outputLabel will be linked to uploaded file, so that User can see the file -->
<p:outputLabel style="cursor: pointer" value="View uploded Script"
rendered="#{c.hidden}" />
</p:outputPanel>
</p:column>
<p:column>
<p:inputText id="testname" value="#{c.testName}"
style="width:90%">
<p:watermark for="testname" value="#{msg.TEST_NAME}" />
</p:inputText>
</p:column>
<p:column>
<p:spinner id="threads" value="#{c.threads}" min="1" max="500"
size="8">
<p:tooltip for="threads" value="#{msg.TEST_NAME}"
showEffect="slide" hideEffect="slide" />
</p:spinner>
</p:column>
<p:column>
<p:selectBooleanCheckbox id="chkSelected" value="#{c.selected}">
<p:tooltip for="chkSelected" value="#{msg.CHECKBOX}"
showEffect="slide" hideEffect="slide" />
</p:selectBooleanCheckbox>
</p:column>
</p:row>
</p:panelGrid>
</ui:repeat>
<p:toolbar style="margin-top: 10px;">
<p:toolbarGroup align="right">
<p:commandButton value="#{msg.ADD_IT}" update="agentForm"
actionListener="#{agent.addComponent()}" />
<p:commandButton value="#{msg.DELETE_IT}" update="agentForm"
actionListener="#{agent.deleteComponent()}" />
</p:toolbarGroup>
</p:toolbar>
</div>
</p:panel>
</h:form>
<p:dialog id="uploadDialog" widgetVar="uploadDialogWidget" header="File upload">
<h:form rendered="#{!empty agent.currentScenario}">
<p:fileUpload
label="Upload Script" style="font-size: 100% !important;"
showButtons="false"
fileUploadListener="#{agent.upload}"
mode="advanced" auto="true" sizeLimit="100000"
allowTypes="/(\.|\/)(py|txt)$/"
update=":agentForm">
</p:fileUpload>
<p:commandButton value="Cancel" onclick="uploadDialogWidget.hide();" onstart="return false;" />
</h:form>
</p:dialog>
You should consider to move from <p:panelGrid to a <p:dataTable which has a built in mechanism to work with row selection.
I have following sample code.
Initially, only commandButton Two is visible. When I click this button, commandButton One is also visible. But when I click One, the backing-bean method click1 does not get fired.
Following is my code:
xhtml
<h:form id="form1">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}" />
</h:form>
<h:form id="form2">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button2" value="Two" action="#{bean.click2}" />
</h:form>
backing-bean
#RequestScoped
#Named("bean")
public class JsfTrial implements Serializable {
private static final long serialVersionUID = 2784462583813130092L;
private boolean show1; // + getter, setter
public String click1() {
System.out.println("Click1()");
return null;
}
public String click2() {
System.out.println("Click2()");
setShow1(true);
return null;
}
}
I found a very informative answer by BalusC.
h:commandLink / h:commandButton is not being invoked
If I understand it correctly, my problem is due to point 5 of this answer.
Does that also mean we can not use hidden commandButton with #RequestScoped backing-bean?
You can use the request scope, you should only pass the condition as a request parameter to the subsequent requests by <f:param> instead of by a JSF hidden input field <h:inputHidden>. The value of the hidden input field is only set in the model during Update Model Values phase, while the condition of the rendered attribute is already evaluated during Apply Request Values phase which is earlier.
So, use <f:param> instead of <h:inputHidden>:
<h:form id="form1">
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
<h:form id="form2">
<h:commandButton id="button2" value="Two" action="#{bean.click2}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
This way you can extract them as request parameter in bean's (post)constructor.
public JsfTrial() {
String show1 = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("show1");
this.show1 = (show1 != null) && Boolean.valueOf(show1);
}
Ugly, but CDI doesn't offer a builtin annotation which substituties JSF's #ManagedProperty("#{param.show1}"). You could however homegrow such an annotation.
As I'm new to JSF framework, wanted to know how to add the user entered data to the database.
More clearly, i'm using jsf with jsp, my jsp contains some input fields and a submit button.How do i capture all the user entered data and send it as input paramters for the button, as my back end takes all these as input parameters and updates the table with a new record.
Please look into my code and lety me know my mistake
Registration.jsp
<body>
<f:view>
<h:form>
<h:panelGrid columns="2" rules="all" width="100%" style="background:#03547C;color:#FDD017">
<h:column>
<h:outputText value="Stu No : "></h:outputText>
<h:inputText value="#{RegBean.stuNo}"/>
</h:column>
<h:column>
<h:outputText value="Stu Name : "></h:outputText>
<h:inputText value="#{RegBean.stuName}"/>
</h:column>
<h:column>
<h:outputText value="Standard : "></h:outputText>
<h:inputText value="#{RegBean.standard}" />
</h:column>
<h:column>
<h:outputText value="School : "></h:outputText>
<h:inputText value="#{RegBean.school}" />
</h:column>
</h:panelGrid>
<h:panelGrid columns="2" rules="all" width="100%" style="background:#03547C;color:#FDD017">
<h:column>
<h:form>
<h:commandButton id="submitBtn" value="Submit" action="#{RegBean.submitDetails}">
<f:param name="sNo" value="#{RegBean.stuNo}" />
<f:param name="sName" value="#{RegBean.stuName}" />
<f:param name="std" value="#{RegBean.standard}" />
<f:param name="schl" value="#{RegBean.school}" />
</h:commandButton>
</h:form>
</h:column>
</h:panelGrid>
</h:form>
</f:view>
</body>
Registration Bean
public class VendorRegBean {
private String stuNo;
private String stuName;
private String standard;
private String school;
// getters and setters
public void submitDetails() {
Map requestMap = context.getExternalContext().getRequestParameterMap();
String stNo = (String) requestMap.get("sNo");
String stName = (String) requestMap.get("sName");
String stndrd = (String) requestMap.get("std");
String scl = (String) requestMap.get("schl");
vReg.stuRegistration(stNo ,stName ,stndrd ,scl );
}
}
You don't need to get the parameters from the request parameter map as long as you register your bean as managed bean and provide getter and setter methodes for your bean members.
Use annotations for your bean to declare it as managed bean. Getter and setter example are given for stuNo member:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class RegBean {
private String stuNo;
...
private String getStuNo() {
return stuNo;
}
private String setStuNo(String stuNo) {
this.stuNod = stuNo;
}
}
In the view you have to reference the managed bean with first letter lower case, such as:
<h:inputText value="#{regBean.stuNo}"/>
Your command button doesn't need <f:param>, simply use:
<h:commandButton id="submitBtn" value="Submit" action="#{regBean.submitDetails}"/>
Then all parameters are automatically available in your submitDetails method and you don't need to get them from the parameter map.