Unable to ajax update component with p:commandLink click - jsf

I have three options for user: two equipment selection and one option to say I don't want equipment. To select two equipment I used p:commandLink. To say I do not want equipment I used h:selectOneRadio.
If user do not want to buy equipment, user can select the option button 'I do not wish to have'. So at anytime, only one button can be selected within the three of them.
If I press h:selectOneRadio button, then other two buttons (p:commandLink) are updating properly to 'select'. But if I select an equipment (p:commandLink), then h:selectOneRadio button is not deselecting even though it's bean value is updated to false.
JSF page
<h:panelGroup id="pwpPanel">
<div class="row">
<ui:repeat value="#{reconBean.EQList}" var="equipment">
<div class="col-md-4">
<div class="btn-fibre-tab text-center">
<ui:fragment rendered="#{reconBean.pwpSelectedEquipmentID != equipment.id }">
<p:commandLink id="selectpwp" process="#form" update=":mainForm:pwpPanel"
styleClass="select_button" action="#{reconBean.updatePWPItem(equipment.id)}"
immediate="true">
Select
<f:ajax render=":mainForm:pwpPanel" />
</p:commandLink>
</ui:fragment>
<ui:fragment rendered="#{reconBean.pwpSelectedEquipmentID eq equipment.id }">
<p:commandLink id="selectedpwp" process="#form" update=":mainForm:pwpPanel"
styleClass="selected_button" action="#{reconBean.updatePWPItem(equipment.id)}"
immediate="true">
Selected
<f:ajax render=":mainForm:pwpPanel" />
</p:commandLink>
</ui:fragment>
</div>
</div>
</ui:repeat>
</div>
<div class="row">
<div class="col-sm-12 ">
<h:selectOneRadio value="#{reconBean.notPurchaseWithPurchase}" id="radioDoNotWant"
name="pwpEquipment">
<f:selectItem itemLabel="I do not wish to have" itemValue="true" />
<f:ajax event="click" execute="#this" listener="#{reconBean.resetPWPSelection}"
render="pwpPanel" />
</h:selectOneRadio>
</div>
</div>
</h:panelGroup>
Bean values
public void updatePWPItem(String itemID) {
pwpSelectedEquipmentID = itemID;
notPurchaseWithPurchase = false;
}
public void resetPWPSelection(AjaxBehaviorEvent event) {
pwpSelectedEquipmentID = null;
}

The problem is the immediate attribute set to true on the CommandLinks. It is essentially to understand the The Lifecycle of a JavaServer Faces Application. The impact of the immediate attribute differs in, on what kind of component it is applied. As stated in a good article:
In the standard JSF lifecycle, the action attribute on an Command component is evaluated in the Invoke Application phase
... if you set immediate="true" on it.
To be honest, i do not exacly know why this prevents your radio option from being unchecked (I am still learning all aspects of the JSF lifecylce), but i think it is because it remains unchanged in the component tree and gets rendered as it was even if the model is updated through your action (if someone may comment on this i will update accordingly).
The solution to your problem is to remove the immediate attribute from your CommandLinks or set it to false.
Some remarks:
PrimeFaces CommandLinks have build in AJAX capabilities which are enabled by default. There is no need to enclose an additional <f:ajax />. Only if you want to disable AJAX you have to set its ajax attribute to false. The render attribute of Ajax conforms to the update attribute of CommandLink.
You are using SelectOneRadio in a context it is not designed for. As stated on JSFToolbox:
This component is designed for situations where you want to display a mutually exclusive list of options to the user as a set of radio buttons.
Consider using another better suited component like CommandLink or CommandButton.

Related

<h:commandButton> Create a new CDI #ViewScoped on Click, instead of using existing one

In a JSF 2.2 application, a user visit the link test.xhtml?id=324 , The page must have the paramater id. The page is backed by a CDI #ViewScoped Bean called TestBean.
The fields get initiliazed using a #postconstruct method
#PostConstruct
public void initialize() {…}
and this method that, load data based on id, if id is not present or correct, then direct the user to another page
<f:metadata>
<f:viewAction action="#{testBean.redirectNoParameters}"></f:viewAction>
</f:metadata>
public String redirectNoParameters() {
//method load data based on id, if id is not present or correct, then direct the user to another page
test = testDao.find(id);
In this page there is a comment section
<h:form role="form" prependId="false" >
<h:selectOneMenu value="#{testBean.sortComment}" valueChangeListener="#{testBean.orderComments}">
<f:ajax event="change" execute="#this" render="comment-listing" />
<f:selectItem itemLabel="Recent" itemValue="1"></f:selectItem>
<f:selectItem itemLabel="Oldest" itemValue="2"></f:selectItem>
<f:selectItem itemLabel="Popular" itemValue="3"></f:selectItem>
</h:selectOneMenu>
</h:form>
<div jsf:id="comment-listing">
<div >
<h:form role="form" prependId="false">
<ui:repeat var="comment" value="#{testBean.comments}" >
<div >
<div >
<div>
<h:commandButton value="#{comment.votes.size()}" action="#{testBean.upVote(comment)}" >
<f:ajax execute="#this" render="comment-listing" immediate="true"/>
</h:commandButton>
</div>
</div>
<div >
<h4 >
<h:link outcome="channel">
<f:param name="handle" value="#{comment.commentor.handle}"></f:param>
#{comment.commentor.handle}
</h:link>
on <span class="c-date">#{comment.creationDate}</span>
</h4>
#{comment.comment}
</div>
</div>
</ui:repeat>
</h:form>
</div>
</div>
As You can see for each comment there is a <h:commandButton> for a user to upvote the comment (like in youtube, stackoverflow…) . When the method is clicked it works as inteded. The method testBean.upVote(comment), is called and the ui is updated.
THEN I use the first form above to Re-order comments, say for example the change is from popular -> Recent. Also the method does its job and the ui is updated.
Till now all is working great.
However, When I click the upvote btn/method (of a different comment) AGAIN. The contsructor get called, so is #postconstruct, and the redirectNoParameters().
I have tried removing all ajax tag to see if it is an ajax problem. And The problem still occurs. So it’s not an ajax issue.
I have tried remove the parameter comment from the method "#{testBean.upVote(comment)}" to see if that’s what producing the problem. And The problem still occurs.
I have tried using <c:forEach instead of <ui:repeat . Still no hope.
What’s produce the problem? Why is this happening instead of just calling the method of the command button ?? Thanks.

Disabling items in JavaServer Faces

I’m new in JavaServer Faces.
I have a simply form:
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">#{msgs.leavereply}</h3>
<h1>#{!forumsBean.activeWithProject}</h1>
<h:form>
<h:inputTextarea value="#{forumpostsBean.text}" style="resize:none"/>
<h:commandButton value="#{msgs.add}" action="#{forumpostsBean.addForumpost}"/>
</h:form>
</div>
By clicking command button all it’s fine, executes method forumpostsBean.addForumpost.
But when I modify code to this:
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">#{msgs.leavereply}</h3>
<h1>#{!forumsBean.activeWithProject}</h1>
<h:form>
<h:inputTextarea value="#{forumpostsBean.text}" style="resize:none" disabled="#{not forumsBean.active}"/>
<h:commandButton value="#{msgs.add}" action="#{forumpostsBean.addForumpost}" disabled="#{not forumsBean.active}"/>
</h:form>
</div>
When items not disabled, current page only have refreshed, but method forumpostsBean.addForumpost don’t executes.
In both variants, <h1>#{!forumsBean.activeWithProject}</h1> shows correct value.
You have 2 beans? one called forumpostsBean and another just forumsBean? I can't see if you've got them both but maybe its the main error? as the comment above says, maybe your logic is out of place.
You may want to use rendered="" on the button rather that just disabling it so its not part of the dom at all upon page load and then use an ajax call when you type data in your input field to do partial ajax render the div again and show it. good for testing.

Confusion JSF Updating Value of Input text in Backing bean

Today, i was solving a small problem in JSF. Actually, i have an InputText on my xhtml page with readyOnly attribute set to true.Requirement was on click of a boolean checkbox, i should make the input text editable, means readyOnly false. The code somewhat seemed like this.
<div class="div-form-col" >
<h:outputLabel value="#{msgs.violationAmount} " for="violationAmt" />
<h:inputText value="#{outpassMBean.violationWebDTO.violationAmount}" id="violationAmt" readonly="true"/>
</div>
<div class="div-form-row">
<div class="div-form-col">
<h:outputLabel value="#{msgs.violationExemptionApplicable} " for="violationExempted" />
<h:selectBooleanCheckbox value="#{outpassMBean.violationWebDTO.violationExemptionApplicable}" id="violationExempted" onchange="makeViolationAmountEditable(this);">
</h:selectBooleanCheckbox>
</div>
</div>
The script of that method was given as below :-
function makeViolationAmountEditable(id){
alert("Ben");
document.getElementById('violationAmt' ).readOnly=false;
alert("Done");
}
Now my first problem was, if i am editing the value in the text field, how i can update the value of violationAmt in backing bean. What can be the best possible way? As i am using PrimeFaces, i came across concept remoteCommand. So here is what i added.
<p:remoteCommand name="makeViolationAmountEditableOnServer" action="#{outpassMBean.makeViolationAmountEditable}" update="amountPanelApplicant"/>
The method at the backing bean level was something like this
public void makeViolationAmountEditable(){
//Set updated value
setUpdatedViolationAmount(violationWebDTO.getViolationAmount());
//Some other code.
}
Problem was that, whenever this code was running, the violation amount in violationWebDTO was the old one, not the one which i was entering after making the input field editable. Though i saw in firebug the updated value was part of request but in the backing bean, however still old value was getting referred. I don't understand why?
My senior told me, you are updating value of readOnly on client side not server side, and updated my code something like this.
<p:outputPanel id="amountPanelApplicant">
<p:outputPanel rendered="#{outpassMBean.violationWebDTO.violationForCmb eq 2 and outpassMBean.violationWebDTO.isViolationExists}">
<p:outputPanel styleClass="div-form twoCol">
<div class="div-form-row">
<div class="div-form-col" >
<h:outputLabel value="#{msgs.violationAmount} " for="violationAmt" />
<h:inputText value="#{outpassMBean.violationWebDTO.violationAmount}" id="violationAmt" readonly="#{outpassMBean.violationAmtEditable}">
</h:inputText>
</div>
<div class="div-form-col">
<h:outputLabel value="#{msgs.outPayTot} " for="totalViolationAmount" />
<h:outputLabel styleClass="readOnly" value="#{outpassMBean.violationWebDTO.totalViolationAmount}" id="totalViolationAmount" />
</div>
</div>
</p:outputPanel>
<p:outputPanel styleClass="div-form twoCol" rendered="#{outpassMBean.outpassApplication.applicationSubType.id eq 2 }" >
<div class="div-form-row">
<div class="div-form-col">
<h:outputLabel value="#{msgs.violationExemptionApplicable} " for="violationExempted" />
<h:selectBooleanCheckbox value="#{outpassMBean.violationWebDTO.violationExemptionApplicable}" id="violationExempted" onchange="makeViolationAmountEditableOnServer();">
</h:selectBooleanCheckbox>
<p:remoteCommand name="makeViolationAmountEditableOnServer" action="#{outpassMBean.makeViolationAmountEditable}" update="amountPanelApplicant"/>
</div>
</div>
</p:outputPanel>
</p:outputPanel>
public void makeViolationAmountEditable(){
if(logger.isDebugEnabled()){
logger.debug("Inside makeViolationAmountEditable...");
}
//setting violation amount editable flag
setViolationAmtEditable(false);
//Preserving original and total amount.
setOriginalViolationAmt(violationWebDTO.getViolationAmount());
setOriginalTotalViolationAmount(violationWebDTO.getTotalViolationAmount());
}
In the above updated code, no javascript called. The readyOnly value is set to true and false from the backing bean itself. After this update, basically the new edited value was updated in the violationWebDTO.
Can someone explain? Why not in the first snapshot? It's not a complete code but i tried to explain the confusion. Any pointers would be helpful.
This is JSF's safeguard against tampered/hacked requests wherein the enduser uses client side languages/tools like HTML or JS to manipulate the HTML DOM tree and/or HTTP request parameters in such way that the outcome of JSF's disabled, readonly or even rendered attribute is altered.
Imagine what would happen if the JSF developer checked an user's role in such a boolean attribute against the admin role like so disabled="#{not user.hasRole('ADMIN')}" and a hacker manipulated it in such way that it isn't disabled anymore for non-admin users. That's exactly why you can only change the mentioned attributes (and the required attribute and all converters, validators, event listeners, etc) on the server side.
This can however be solved in a simpler manner without the need for an additional property, without the <p:remoteCommand> and the additional bean method.
<h:inputText id="violationAmount" ... readonly="#{makeViolationAmountEditable.value}" />
...
<h:selectBooleanCheckbox binding="#{makeViolationAmountEditable}">
<f:ajax render="violationAmount" />
</h:selectBooleanCheckbox>
See also:
Why JSF saves the state of UI components on server?
How does the 'binding' attribute work in JSF? When and how should it be used?.

Action of <h:commandButton> not getting invoked when "disabled=true" is set

I want the Submit button to be enabled when the T&C checkbox is checked. Though my written logic is working alright the action of h:commandButton is not getting invoked even after it is enabled after checking the checkbox. Initially the button is disabled. The code works fine if I remove the disabled="TRUE" attribute but it doesn't serve the purpose. Here is my code :
<script type="text/javascript">
$(document).ready(start);
function start() {
$('.testing').click(
function() {
if ($(document.getElementById('form2:check'))
.is(':checked')) {
$(document.getElementById('form2:saveForm'))
.removeAttr('disabled');
} else {
$(document.getElementById('form2:saveForm')).attr(
'disabled', 'disabled');
}
});
}
</script>
JSP Code:
<h:form id="form2">
<div class="testing">
<h:selectBooleanCheckbox id="check" />
Agree
<li class="buttons ">
<div class="center">
<h:commandButton id="saveForm" styleClass="btTxt submit"
type="submit" value="Submit"
action="#{declarationFormBean.formSubmit}" disabled="true"></h:commandButton>
</div>
</li>
</h:form>
Please help.
That's because you're solely removing the disabled attribute in the JSF-generated HTML representation using JavaScript/jQuery. This doesn't change anything to the disabled attribute in the actual JSF component which is consulted during decoding the action event.
This is actually a Good ThingTM of JSF, because otherwise any hacker would be able to bypass any JSF disabled, readonly and rendered attribute by just manipulating the HTML DOM tree while they are initially used to block them like so rendered="#{request.isUserInRole('ADMIN')}".
You need to enable the button by JSF instead of JS/jQuery.
<h:selectBooleanCheckbox binding="#{check}">
<f:ajax render="saveForm" />
</h:selectBooleanCheckbox>
<h:commandButton id="saveForm" ... disabled="#{not check.value}" />
That's all. No custom JS/jQuery mess necessary, nor any additional JSF bean properties. The above code is complete as-is.
See also:
What is the need of JSF, when UI can be achieved from CSS, HTML, JavaScript, jQuery?

How to render an h:panelGroup correctly when using f:ajax and a rendered attribute?

I'm developping an application in JSF2.0. I encountered a problem when mixing ajax and a panelGroup with a rendered attribute.
My page contains 3 panels. In the first panel the user selects an item from a <h:selectOneMenu>. This causes to render the second panel.
The second panel contains an h:dataTable that is connected to a DataModel in the backing bean. The values of the dataTable are determined by the item that was chosen in the <h:selectOneMenu> of the first panel. Each row in the table contains a commandLink to view detailed info about the item of that row:
<h:commandLink action="#{faseController.selectInvulling}">
<f:ajax render="#form" execute="#form" />
</h:commandLink>
Clicking on the commandLink the third panel gets rendered:
<h:panelGroup rendered="#{faseController.invullingSelected}" layout="block" styleClass="panelElement" id="invulling">
<label>
<span>Datum:</span>
<h:inputText value="#{faseController.selectedInvulling.datum}" required="true" requiredMessage="Gelieve een datum in te vullen." converterMessage="Gelieve een geldige datum in te vullen (dd/mm/jjjj)." >
<f:convertDateTime pattern="dd/MM/yyyy" />
</h:inputText>
</label>
<label>
<span>Evaluatie</span>
<h:inputTextarea value="#{faseController.selectedInvulling.evaluatie}" cols="100" />
</label>
<label>
<span>Methode</span>
<h:inputTextarea value="#{faseController.selectedInvulling.methode}" cols="100" />
</label>
</h:panelGroup>
This is where I encounter a problem. The first time I click on a commandLink to view the detailed info about that row in the table, I get the correct data in the third panel. Afterwards I change the item in the <h:selectOneMenu> of the first panel: the correct corresponding dataTable gets rendered in the second panel. However this time when I click on a commandLink in the table to view the details of that item, I get to see the info from the item I clicked the first time.
If I ommit rendered="#{faseController.invullingSelected}" everything works fine, but this causes confusion in the view, since the third panel is now always rendered.
All three panels are located in the same form. My backing bean is ViewScoped. What am I doing wrong? Any help woud be greatly appreciated.
This can happen if you're doing business logic in an getter method instead of in an (action)listener method. I also wonder if you really need to execute and render the entire form. Try to be more specific in specifying the elements which are to be executed and/or re-rendered.
The question is way too broad and the code is not complete enough in order to pinpoint the real cause and propose the a ready-to-use solution.
Try using this.
<a4j:commandLink action="#{faseController.selectInvulling}" execute="#this"
render="thirdPanelId" limitRender="true"/>
Execute only the components which you want to process and render only the components which needs to be refreshed.

Resources