Change the properties of an Input within a ui:repeat - jsf

I'd like to change the "required" property of an InputText that is located within an ui:repeat, but I'm not able to access to the component from the ManagedBean:
<h:selectManyCheckbox id="required" value="#{test.required}"
layout="lineDirection" converter="javax.faces.Integer">
<f:ajax event="change" listener="#{test.update}" />
<f:selectItems value="#{test.selectable}"></f:selectItems>
</h:selectManyCheckbox>
<ui:repeat value="#{test.names}" var="name" id="repeat">
<h:panelGrid columns="3">
<h:outputLabel id="nameLabel">name:</h:outputLabel>
<h:inputText id="name" value="#{name}"
validator="#{test.validateName}" />
<h:message for="name"></h:message>
</h:panelGrid>
</ui:repeat>
I'm trying to use the findComponent method, but it does not work:
public void update(AjaxBehaviorEvent event) {
for(Integer i: selectable) {
UIViewRoot vr = FacesContext.getCurrentInstance().getViewRoot();
HtmlInputText input = (HtmlInputText)vr.findComponent("form:repeat:"+i+":name");
input.setRequired(required.contains(i));
}
}

The ui:repeat doesn't repeat the components in the view root, it repeats the component's output in the rendered HTML output.
There are several ways to achieve this properly. One of them is to use a value object instead and set the requireness there. E.g. a List<Item> wherein Item has the properties String name and boolean required.
<ui:repeat value="#{test.items}" var="item" id="repeat">
<h:panelGrid columns="3">
<h:outputLabel id="nameLabel">name:</h:outputLabel>
<h:inputText id="name" value="#{item.name}" required="#{item.required}" validator="#{test.validateName}" />
<h:message for="name"></h:message>
</h:panelGrid>
</ui:repeat>
There are more ways, but since the JSF version you're using and the functional requirement is unclear, it's only guessing which way is the most applicable in your case.

Related

Pass <p:selectOneListbox var="ch"> as argument of <h:commandLink action>

I'm facing problem with passing object to backing bean from actionListener in commandLink. What I have, is a bean MeasureBean and a xhtml file, that uses the bean.
In the xhtml I have:
<p:selectOneListbox var="ch">
<f:selectItems value="#{MeasureBean.checkpoints}" var="cp" itemValue="#{cp}" />
<p:column>
<h:outputText value="#{ch.name}" />
</p:column>
<p:column>
<h:commandLink actionListener="#{MeasureBean.onCheckpointRemoved(ch)}">
<h:graphicImage library="#{ctx.theme}" name="images/delete.gif" />
<f:ajax event="click" />
</h:commandLink>
</p:column>
</p:selectOneListbox>
In the bean I have method:
public void onCheckpointRemoved(Checkpoint viewCheckpoint) {
System.out.println(viewCheckpoint);
// TODO
}
The problem is, that regardless of whethert I use the tag <f:ajax> or not, the parameter viewCheckpoint of the method in bean is always null. I need to pass that parameter to the bean. It doesn't have to be the whole object, it is allowed to pass just the id of the checkpoint. What I also tried, was:
<h:commandLink actionListener="#{MeasureBean.onCheckpointRemoved(cp)}">
(cp instead of ch). But no difference.
Please help,
Mateusz
You forgot to set the value of <p:selectOneListBox>. Also, you'll have to create a converter.
You can check an example of converter in PrimeFaces Showcase
<p:selectOneListbox var="ch" value="#{MeasureBean.checkPointTest}" converter="checkPointConverter">
<f:selectItems value="#{MeasureBean.checkpoints}" var="cp" itemValue="#{cp}" />
<p:column>
<h:outputText value="#{ch.name}" />
</p:column>
<p:column>
<h:commandLink actionListener="#{MeasureBean.onCheckpointRemoved(ch)}">
<h:graphicImage library="#{ctx.theme}" name="images/delete.gif" />
<f:ajax event="click" />
</h:commandLink>
</p:column>
</p:selectOneListbox>
Checkpoint checkPointTest;
// getter/setter....
public void onCheckpointRemoved(Checkpoint viewCheckpoint) {
System.out.println(viewCheckpoint);
// TODO
}

conditional rendered f:selectItem possible problems

I have a page with several h:selectOneMenu or p:selectOneMenu and I want to use the same page for editing and adding data.
When I will edit data I need f:selectItem. I know that this component doesn't have attribute rendered. And I read that I can use <c:if>.
Ok. For example, if I write
<p:selectOneMenu rendered="#{not empty bean.id}"
value="#{bean.selectedId}">
<c:if test="${editableBean != null}">
<f:selectItem itemLable="#{editableBean.name} itemValue=#{editableBean.id} />
</c:if>
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
Will it works without any problems in primefaces and with ajax listeners?
The easy solution (but with poor performance) will be to have a boolean editMode attribute in your managed bean to enable/disable the components. Basic example:
<p:selectOneMenu rendered="#{not empty bean.id}" disabled="#{bean.editMode}"
value="#{bean.selectedId}">
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
In your bean
#ManagedBean
#ViewScoped
public class Bean {
private int id;
private boolean editMode;
//other attributes...
//getters and setters...
#PostConstruct
public void init() {
//a way to know if the bean it's in edit mode
editMode = (id != 0);
}
}
This solution will have poor performance because every <p:selectOneMenu> will have to load all the data and then select the actual value, but it will do what you want. Another option will be to use this attribute for the rendered property of <p:selectOneMenu> and for an <h:inputText disabled="true" readonly="true" /> (or maybe <h:outputText />). Another basic sample:
<p:selectOneMenu rendered="#{not empty bean.id && not bean.editMode}"
value="#{bean.selectedId}">
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
<h:inputText rendered="#{bean.editMode}" value="{bean.selectedText}"
disabled="true" readonly="true" />

valuechangelistener does not populate values in text box

Here is my jsp:
<h:selectOneMenu value="#{member.dependentName}" onchange="this.form.submit()"
immediate="true" valueChangeListener="#{member.getDependentAddress}">
<f:selectItems value="#{member.dependentList}" />
</h:selectOneMenu>
<h:inputText value="#{member.personName}" immediate="true" />
<h:inputText value="#{member.dob}" immediate="true" />
And this, the function valuechangelistener fires.
public void getDependentAddress(ValueChangeEvent e) {
setPersonName((getDependentsList().get(e.getNewValue().toString())
.getDependentName()));
setDob(getDependentsList().get(e.getNewValue().toString()).getBirth());
System.out.println("New dob value : " + dob);
System.out.println("New name value : " + personName);
FacesContext.getCurrentInstance().renderResponse();
}
The two sysouts give the new value in the console but once the page loads, the fields are blank. I have tried all scopes for the bean. No go. What am i missing?
Thanks
You missed nothing. You've just something too much. To get it to work, you should remove immediate="true" from the to-be-changed components.
<h:selectOneMenu value="#{member.dependentName}" onchange="this.form.submit()"
immediate="true" valueChangeListener="#{member.getDependentAddress}">
<f:selectItems value="#{member.dependentList}" />
</h:selectOneMenu>
<h:inputText value="#{member.personName}" />
<h:inputText value="#{member.dob}" />
The immediate="true" on an UIInput component will cause its validations phase to take place in apply request values phase instead. This gives you the opportunity to use FacesContext#responseComplete() inside a valueChangeListener method skip other components which doesn't have immediate="true" set from being processed. As you have now, with immediate="true", they are also processed.
Please note that this is essentially a hack from the old JSF 1.x ages. If you're already using JSF 2.x, you should be using <f:ajax listener> instead.
<h:selectOneMenu value="#{member.dependentName}">
<f:selectItems value="#{member.dependentList}" />
<f:ajax listener="#{member.getDependentAddress}" render="name dob" />
</h:selectOneMenu>
<h:inputText id="name" value="#{member.personName}" />
<h:inputText id="dob" value="#{member.dob}" />
with
public void getDependentAddress() {
Dependent dependent = getDependentsList().get(dependentName); // Isn't that actually a Map instead of List?
personName = dependent.getDependentName();
dob = dependent.getBirth();
}

Dynamic input values not updated in backing bean

I have created a dynamic input field which changes depending on the item type.
<h:panelGrid columns="2" cellpadding="10">
<c:forEach items="#{tabVar.items}" var="itmVar">
<h:outputText value="#{itmVar.label}:" />
<c:if test="#{itmVar.isString}">
<p:inputText id="#{itmVar.id}" value="#{itmVar.value}" required="#{itmVar.isEditable}" disabled="#{itmVar.isEditable}" valueChangeListener="#{tabBean.processValueChange}" maxlength="100" size="75" immediate="true" onchange="form1.submit()"/>
</c:if>
<c:if test="#{itmVar.isDate}">
<p:calendar id="#{itmVar.id}" value="#{itmVar.value}" required="#{itmVar.isEditable}" disabled="#{itmVar.isEditable}" valueChangeListener="#{tabBean.processValueChange}" onSelectUpdate="form1.submit();"/>
</c:if>
<c:if test="#{itmVar.isDouble}">
<p:inputText id="#{itmVar.id}" value="#{itmVar.value}" required="#{itmVar.isEditable}" disabled="#{not itmVar.isEditable}" valueChangeListener="#{tabBean.processValueChange}" maxlength="100">
<f:validateDoubleRange minimum="#{itmVar.minDouble}" maximum="#{itmVar.maxDouble}" />
</p:inputText>
</c:if>
<c:if test="#{itmVar.isInteger}">
<p:inputText id="#{itmVar.id}" value="#{itmVar.value}" required="#{itmVar.isEditable}" disabled="#{not itmVar.isEditable}" valueChangeListener="#{tabBean.processValueChange}" maxlength="100">
<f:validateLongRange minimum="#{itmVar.minLong}" maximum="#{itmVar.maxLong}" />
</p:inputText>
</c:if>
</c:forEach>
</h:panelGrid>
Everything renders correctly, but when I change and submit the input values, then it does not get updated in the backing bean. How is this caused and how can I solve it?
#{tabBean.processValueChange} may not right. Its not possiable to use only 1 method for valueChangeListener for all value. Can u post your full code in ManagedBean ? is that on #SessionScope ? The right method for valueChangeListener may be like this:
public void saveStatus(ValueChangeEvent event) {
Integer newValue = (Integer) event.getNewValue();//this is for save Status
itmVar.setStatus(newValue);
}
If you want to save a serial of value, u have to create a serial of method :) good luck!

multiple h:forms in ui:repeat

I'm trying to have multiple h:forms on a page which are rendered with a ui:repeat component. If I have only one generated h:form, everything works perfect. If I have multiple h:forms, only the latest h:form will work! (If I try to submit another form then the last, only null is passed trough my service!)
<ui:repeat var="element" value="#{myService.elementList}">
<rich:simpleTogglePanel switchType="client"
label="#{element.codeElement} - #{element.description}"
opened="#{myService.isUiSelected(element)}"
onexpand="expand#{element.codeElement}()"
oncollapse="collapse#{element.codeElement}()">
<h:form id="newKindForm">
<a:commandLink ajaxSingle="true" id="idLink" onclick="#{rich:component('newTargetPanel')}.show()">
<a:commandButton id="myButton"
value="new Form"
action="#{myService.newForm(element)}"/>
</a:commandLink>
</h:form>
<rich:modalPanel id="newTargetPanel" autosized="true" width="450">
<f:facet name="header">
<h:outputText value="My Pannel Title" />
</f:facet>
<f:facet name="controls">
<h:panelGroup>
<h:graphicImage value="/img/close.png"
id="hidelink"
styleClass="hidelink" />
<rich:componentControl for="newTargetPanel"
attachTo="hidelink"
operation="hide"
event="onclick" />
</h:panelGroup>
</f:facet>
<!-- THIS IS THE PROBLEMATIC FORM -->
<h:form>
<a:include viewId="../myRepeatableForm.xhtml" />
</h:form>
</rich:modalPanel>
</ui:repeat>
Some suggestion?
Thank You
you can contain everything in a single form, and use execute attribute to submit only the elements you want. like this:
<h:form id="newKindForm" prependId="false">
<ui:repeat id="newKindRepeat" var="element"
value="#{myService.elementList}" varStatus="varStatus">
<h:panelGroup id="newKindPanel">
<a:commandButton id="myButton"
execute="newKindRepeat:#{varStatus.index}:newKindPanel"
onclick="#{rich:component('newTargetPanel')}.show()"
value="new Form"
action="#{myService.newForm(element)}"/>
</h:panelGroup>
</ui:repeat>
</h:form>
please notice prependId of h:form, id and varStatus of ui:repeat, execute of a:commandButton.
btw, supposing that a: stands for ajax4jsf, i wouldn't recommend using commandButton nested in commandLink.
Is #{myService.elementList} a List ? If so, I'm not sure if that will work. Try using javax.faces.model.DataModel instead. Be sure to read the documentation carefully. It works something like this:
public Datamodel getElements(){
if(elements == null)
elements = new ListDataModel(getElementList());
return elements;
}
public List<Element> getElementList(){
elementList = foo(); // load the elements in your List like you did before
return elementList;
}
in your view: <ui:repeat var="element" value="#{myService.elements}">
I'm not familiar with RichFaces, this is the only suggestion I can make.

Resources