I want to change the inputTexts' values when I choose another Skin from my selectOneMenu.
Everything is doing well, my Converter gives back the right Object from the menu, but the inputTexts are not updated.
<h:form>
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
onchange="this.form.submit()" converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
</h:selectOneMenu>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}"></h:inputText>
<br />
<h:inputText id="tcolor" value="#{helloBean.currentSkin.titleBar.textColor}"></h:inputText>
<br />
<h:inputText id="bcolor" value="#{helloBean.currentSkin.titleBar.backgroundColorStart}"></h:inputText>
</h:form>
Here is what my Bean looks like. I debugged it and the Object currentSkin is set correctly. Now i need to know how to update the textfields content.
#ManagedBean
#SessionScoped
public class HelloBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<ExtendedSkin> mySkins;
private List<SelectItem> mySkinsSI;
private ExtendedSkin currentSkin;
public void skinValueChanged(ValueChangeEvent e) {
currentSkin = (ExtendedSkin) e.getNewValue();
FacesContext.getCurrentInstance().renderResponse();
}
public List<ExtendedSkin> getMySkins() {
mySkins = XMLParser.readExtendedSkins();
return mySkins;
}
public List<SelectItem> getMySkinsSI() {
mySkinsSI = new LinkedList<SelectItem>();
for (ExtendedSkin s : getMySkins()) {
mySkinsSI.add(new SelectItem(s, s.getTitle()));
}
return mySkinsSI;
}
public void setMySkinsSI(List<SelectItem> myItems) {
this.mySkinsSI = myItems;
}
public ExtendedSkin getCurrentSkin() {
if (currentSkin == null) {
currentSkin = getMySkins().get(0);
}
return currentSkin;
}
public void setCurrentSkin(ExtendedSkin currentSkin) {
this.currentSkin = currentSkin;
}
}
The problem here is that the converter is doing its work filling the helloBean.currentSkin object, but the values in the <h:inputText> that are bounded to this helloBean.currentSkin: title, textColor and backgroundColorStart will be send to the server and replace the actual values that were loaded by the converter. In other words:
The converter is executed and builds the helloBean.currentSkin based on the selected value.
The <h:inputText id="name"> empty value is sent to server and will be injected in helloBean.currentSkin.title. Same behavior for the other 2 <h:inputText>s.
The view will be loaded using the selected helloBean.currentSkin and it will load the helloBean.currentSkin.title with the empty value. Same behavior for the other 2 <h:inputText>s.
There are two possible solutions to this problem:
Move the <h:inputText>s outside the form, so the empty values won't be send to the server. When loading the view, it will maintain the values loaded in the converter.
<h:form>
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
onchange="this.form.submit()" converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
</h:selectOneMenu>
</h:form>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}"></h:inputText>
<!-- rest of Facelets code... -->
Since you're loading the helloBean.currentSkin while changing the selected value on your dropdownlist, you can add ajax behavior using <f:ajax> tag component inside the <h:selectOneMenu> and update the fields in a cleaner way. I would opt for this solution.
<h:form>
<!-- Note that there's no need of the onchange JavaScript function -->
<h:selectOneMenu id="dropdownSkin"
value="#{helloBean.currentSkin}" defaultLabel="Select a skin.."
valueChangeListener="#{helloBean.skinValueChanged}" immediate="true"
converter="SkinConverter" >
<f:selectItems value="#{helloBean.mySkinsSI}" var="c"
itemValue="#{c.value}" />
<f:ajax process="#this" render="name tcolor bcolor" />
</h:selectOneMenu>
<br />
<h:inputText id="name" value="#{helloBean.currentSkin.title}" />
<h:inputText id="tcolor" value="#{helloBean.currentSkin.titleBar.textColor}" />
<br />
<h:inputText id="bcolor"
value="#{helloBean.currentSkin.titleBar.backgroundColorStart}" />
</h:form>
You can learn more about <f:ajax> in online tutorial like this one.
Since you're going to use an ajax call in your page, you should change your managed bean scope from #SessionScoped to #ViewScoped. More info about this here: Communication in JSF 2
Related
I have 4 buttons, each button will display one image using Ajax.
<h:form>
<h:commandButton value="Click" action="#{userAjaxData.toggleStatus}">
<f:ajax render="Family" />
</h:commandButton>
<h:commandButton value="Click" action="#{userAjaxData.toggleStatus}">
<f:ajax render="Karam"/>
</h:commandButton>
<h:commandButton value="Click" action="#{userAjaxData.toggleStatus}">
<f:ajax render="Memo"/>
</h:commandButton>
<h:commandButton value="Click" action="#{userAjaxData.toggleStatus}">
<f:ajax render="Baba"/>
</h:commandButton>
<h:panelGroup id="Family">
<p:graphicImage library="images/img" name="family.png" id="FamilyImg" rendered="#{userAjaxData.status}"/>
</h:panelGroup>
<h:panelGroup id="Karam">
<p:graphicImage library="images/img" name="Karam.png" id="KaramImg" rendered="#{userAjaxData.status}"/>
</h:panelGroup>
<h:panelGroup id="Memo">
<p:graphicImage library="images/img" name="memo.png" id="MemoImg" rendered="#{userAjaxData.status}"/>
</h:panelGroup>
<h:panelGroup id="Baba">
<p:graphicImage library="images/img" name="baba.png" id="BabaImg" rendered="#{userAjaxData.status}"/>
</h:panelGroup>
</h:form>
My manged bean:
#ManagedBean(name = "userAjaxData", eager = true)
#SessionScoped
public class UserAjaxData implements Serializable {
private static final long serialVersionUID = 1L;
private boolean status = false;
public void toggleStatus() {
status = true;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}}
when I click any button It will display the related image, so if I click the first one it will display the first image, and the second button will display the second image but the first image will not removed. I just need one way to display one image each time I click button. so if I click the first button then the second button should the second image displayed and the first one disappear.
How can I mange it?
If your usecase is really that simple, I would recommend using only one always rendered <h:graphicImage /> (no need for PrimeFaces GraphicImage in this case) and set the appropriate image to show in the backing bean via ajax instead of one or multiple toggle statuses with something like this:
<h:form id="form">
<h:commandButton
id="familyButton"
action="#{toggleImageView.setImage('family.png')}"
value="Family"
>
<f:ajax render="image" />
</h:commandButton>
<h:commandButton
id="karamButton"
action="#{toggleImageView.setImage('karam.png')}"
value="Karam"
>
<f:ajax render="image" />
</h:commandButton>
<h:commandButton
id="memoButton"
action="#{toggleImageView.setImage('memo.png')}"
value="Memo"
>
<f:ajax render="image" />
</h:commandButton>
<h:commandButton
id="babaButton"
action="#{toggleImageView.setImage('baba.png')}"
value="Baba"
>
<f:ajax render="image" />
</h:commandButton>
<br />
<h:graphicImage
id="image"
library="image/img"
name="#{toggleImageView.image}"
/>
</h:form>
#ManagedBean
#RequestScoped
public class ToggleImageView {
private String image;
#PostConstruct
public void initialize() {
image = "family.png";
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}
Just adapt to your needs.
I created a very simple example based on my project in order to illustrate my doubt. Just a way to register a person with a list of telephone numbers.
MainController.java
private String name;
private List<Phone> phoneList;
// Getters and Setters
#PostConstruct
private void init() {
phoneList = new ArrayList<>();
}
public static class Phone implements Serializable {
private String number;
// Getters and Setters
#Override
public String toString() {
return number != null ? number : "null";
}
}
public void add() {
phoneList.add(new Phone());
}
public void save() {
System.out.println("Name: " + name + "; " + phoneList.toString());
}
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
In my example, note that all phone fields that are added MUST be filled in (required = true).
The problem is: when I type name and click add (to add a phone) the value of the field is maintained. But when I type a first phone and click add, the phone's value is not maintained. This occurs for all fields within the component ui:repeat.
Is there a way to preserve the input values within a after an immediate request, as with the name field?
Extra note: Other strange behavior I noticed is when add at least two phone fields, let the first blank and fills the second, and saves the form. After a failed validation (due to phone blank), click add will make all fields are filled with the value of the second phone.
Wildfly 9.0.2, JSF Api (Jboss) 2.2.12
Thanks to #BalusC comment. The OmniFaces library has two taghandlers that can be used in this case. In both cases input values will be preserved in case of validation failure. Note that h:commandButton should be with <h:commandButton immediate="false" />.
ignoreValidationFailed
In this case all validation failures will be ignored (including converter failures). Note that the h:form have to be changed to o:form. Also, the failures messages will still be displayed, which can be solved putting a proper condition in the rendered attribute. The files will look like this:
index.xhtml
<o:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:ignoreValidationFailed />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</o:form>
<h:messages rendered="#{facesContext.validationFailed}" />
skipValidators
In this case only the validation failures will be ignored (the converters will still run). The failures messages will not be displayed, except for the converters. Note that this taghandler is only available since the 2.3 version. The files will look like this:
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:skipValidators />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
The solution that I use to this problem is to create an external field to the loop, which stores a JSON containing the values that should be saved. This field, to be outside the loop, properly saves values after each try and restore the missing values when necessary. I use two functions JavaScript and JQuery library.
So the files would look like this:
index.xhtml
<h:outputScript library="jquery" name="jquery.min.js" />
<h:outputScript library="all" name="all.js" />
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText styleClass="savePhoneNumber" value="#{phone.number}" required="true" onchange="saveUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')" />
</ui:repeat>
<h:inputHidden id="allPhoneNumber" binding="#{allPhoneNumber}" />
<h:outputScript>loadUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')</h:outputScript>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
all.js
function saveUiRepeatInput(inputAll, inputClass) {
document.getElementById(inputAll).value = JSON.stringify($('.' + inputClass).map(function() { return this.value; }).get());
}
function loadUiRepeatInput(inputAll, inputClass) {
var jsonAll = document.getElementById(inputAll).value;
if (jsonAll) {
var array = JSON.parse(jsonAll);
$('.' + inputClass).each(function(i) { if (i < array.length) this.value = array[i]; });
}
}
Although work perfectly (including via ajax, with some minor changes), it looks like a hack, not an ideal solution. So if anyone can help with any solution strictly based on JSF, I will be grateful. Thanks.
I populated my selectOneMenu with objects and trying to send the id (in itemValue) back to my bean from the selected item, I tried it using the below functionality but I keep getting error about a null converter (Which I'm trying to avoid by sending the id to my bean).
xhtml:
<h:form>
<h:selectOneMenu value="#{bean.id}">
<f:selectItems value="#{bean.objectList}" var="f" itemValue="#{f.id}" itemLabel="#{f.name}" />
</h:selectOneMenu>
<h:commandButton action="#{bean.function}" value="OK"/>
</h:form>
bean:
private Collection<Object> objectList; //Object is an example, It is not the real class that is used
private int id;
public void function() {
// place where id is needed.
}
// id getters & setters
The collection you are using:
private Collection<Object> objectList;
Has a plain object as the class type for its elements.
You need to change it to:
private Collection<MyCustomClassElement> objectList;
Where MyCustomClassElement is the class for your collection elements used in the select.
I think the value is not updated, use ajax to update the value once you select it...
<h:form id="forms">
<h:selectOneMenu id="beanid" value="#{bean.id}">
<f:selectItems value="#{bean.objectList}" var="f" itemValue="#{f.id}" itemLabel="#{f.name}" />
<f:ajax event="change" render="forms" />
</h:selectOneMenu>
<h:commandButton action="#{bean.function}" value="OK"/>
</h:form>
I have a JSF page that displays numerous data items pulled from a DB given an account. The page has a SelectOneMenu that lists all accounts in the DB to choose from and a commandButton to trigger reloading the page with the newly selected account. My issue is when I click the commandButton the page reloads but is not taking the newly selected account. The "else" in method pullData() is being executed.
My JSF page:
<f:metadata>
<f:viewParam name="account" value="#{accountAnalysisBean.account}" />
<f:viewParam name="start" value="#{accountAnalysisBean.start}" />
<f:viewParam name="end" value="#{accountAnalysisBean.end}" />
<f:event type="preRenderView" listener="#{accountAnalysisBean.pullData}" />
</f:metadata>
<h:form>
<p:panelGrid columns="6">
<f:facet name="header">
<p:selectOneMenu id="pickAccount" value="#{accountAnalysisBean.pickAccount}" required="true">
<f:selectItems value="#{editAccountBean.allAccounts}" />
</p:selectOneMenu>
<p:calendar id="start" value="#{accountAnalysisBean.start}" />
<p:calendar id="end" value="#{accountAnalysisBean.end}" />
<p:commandButton value="Get Account" actionListener="#{accountAnalysisBean.updateAccount}" />
<f:param name="start" value="#{accountAnalysisBean.start}"/>
<f:param name="end" value="#{accountAnalysisBean.end}"/>
<f:param name="account" value="#{accountAnalysisBean.pickAccount}"/>
</p:commandButton>
</f:facet>
</p:panelGrid>
</h:form>
My Bean:
#ManagedBean
#RequestScoped
public class AccountAnalysisBean {
private String account = "";
private String id = "";
private String pickAccount = "";
private Date start;
private Date end;
//----getters and setters----
public void pullData() {
if (account != "") {
//pull various fields from database given account value
} else {
//set default null values
}
}
public String updateAccount(ActionEvent actionEvent) {
fix = DAOFactory.getInstance("fix_site2.jdbc");
datalist = fix.getMyDataDAO();
account = datalist.findAccountID(pickAccount); //returns an ID from a DB given account selected from menu
return "/AccountAnalysis.xhtml?includeViewParams=true";
}
//remainder of code omitted for clarity
}
I am using JSF 2.2 and PrimeFaces 4
Let's walk through what's likely happening here:
<f:viewParam/> will set the value in your AccountAnalysisBean bean on initial page load, using the defined setter
As soon as the page is done loading, AccountAnalysisBean is destroyed: it's a #RequestScoped bean.
So, as at the time you're clicking on your commandButton, you're starting with a fresh copy of AccountAnalysisBean with nothing in it.
Consider:
Making AccountAnalysisBean a #ViewScoped bean
Getting rid of that actionListener and use the action attribute instead. Going by the code sample you have here, you don't really need it.
I have a h:inputText with valueChangeListener, when the user type some code another h:inputText display data from MySQL about that code, the valueChangeListener works but the second h:inputText not displayed the value and only do it when I set the readonly attribute or I change the component to h:outputText
my facelets page is:
<h:form id="idFacturacion">
<rich:panel>
<f:facet name="header">
<h:outputText value="FACTURACION AL CLIENTE" />
</f:facet>
<h:panelGrid columns="4">
<h:outputText value="Cedula: " />
<h:inputText value="#{facturaBean.encFactura.cedula}" onchange="submit();" valueChangeListener="#{facturaBean.processValueChange}" />
<h:outputText value="Nombre: " />
<h:inputText value="#{facturaBean.encFactura.nombre_cli}" />
</h:panelGrid>
</rich:panel>
</h:form>
facturaBean is:
#ManagedBean
#SessionScoped
public class FacturaBean {
private EncFactura encFactura = new EncFactura();
//getter and setter
public void processValueChange(ValueChangeEvent event){
String ced = event.getNewValue().toString();
try{
//do the database thing
if(resultSet.next()){
encFactura.setNombre_cli(resultSet.getString("nombre_cli"));
}else{
encFactura.setNombre_cli("");
}
}catch(SQLException error){
facesContext.addMessage(null, new FacesMessage("Hubo un error SQL."));
}
}
}
Please see
Change inputText value from listener method… and
Possible to execute `valueChangeListener` for `p:inputText` without hitting `enter` key?
May I suggest using ajax?
Here is a primefaces example but you could apply to richfaces..
<h:inputText value="#{facturaBean.stringOne}" >
<p:ajax event="change" listener="#{facturaBean.processValueChange}" update="strTwo"/> </h:inputText> <h:outputText value="Nombre: " />
<h:inputText id="strTwo" value="#{facturaBean.stringTwo}" />
</h:panelGrid>
private String stringOne= "";
private String stringTwo= "";
public void processValueChange(){
stringTwo = stringOne;
}
With getters etc.. basically on change, fires off to ajax, you do your database call etc, then it returns the response and updates your other input field, it's a much cleaner way than trying to submit forms etc..
Also are you sure you want session scope?