understanding ajax execute behavior with datatable using jsf 2.0 - ajax

i'm trying to use an search button which brings back the selected items (can be more than one) and it updates the datatable. Then i have a selectBooleanCheckbox next to each colomn, when user selects "n" items then presses the Select the checked Items it insert DB.
The code can be seen below:
<h:panelGrid columns="5">
<h:form>
<h:outputText value="Item Name"/>
<p:inputText value="#{StockController.itemName}"/>
<h:commandButton value="Search" action="#{StockController.Search}">
<f:ajax execute="#form" render=":results"/>
</h:commandButton>
</h:form>
//The code sample below belongs to BalusC see the post here
<h:panelGroup id="results">
<h:form>
<h:dataTable value="#{bean.entities}" var="entity">
<h:column>
<h:selectBooleanCheckbox value="#{bean.checked[entity.id]}" />
</h:column>
...
</h:dataTable>
<h:commandButton value="Select the checked Items" action="#{StockController.insertDao}" >
<f:ajax execute="#form" render=":results"/>
</h:commandButton>
</h:form>
</h:panelGrid>
Now, i have read many blogs and Core javaServer Faces 3, i don't think there is logical error in ajax usage. I tested by removing each ajax then both works fine but whenever i try to use both of cummondButtons with ajax, the second one "Select the checked Items" does not even call the "StockController.insertDao" method.
Any help is appreciated.
Thanks all.

When you want to ajax-render content which in turn contains another form, then you need to include the ID of that form in the render attribute as well, otherwise the other form will lose its view state and nothing will be processed on submit.
<h:form>
...
<h:commandButton value="Search" action="#{StockController.Search}">
<f:ajax execute="#form" render=":results :resultsForm" />
</h:commandButton>
</h:form>
<h:panelGroup id="results">
<h:form id="resultsForm">
...
</h:form>
</h:panelGroup>
If there's however nothing which goes outside <h:form> inside the same <h:panelGroup>, then you can just omit the <h:panelGroup> altogether and re-render the form directly.
<h:form>
...
<h:commandButton value="Search" action="#{StockController.Search}">
<f:ajax execute="#form" render=":results" />
</h:commandButton>
</h:form>
<h:form id="results">
...
</h:form>
This is related to JSF spec issue 790.
See also:
Communication in JSF2 - Ajax rendering of content which contains another form

Related

JSF panelGroup not getting rendered after ajax call

I'm using JSF and I have a form that contains a panelGroup and this panelGroup contains two commandLinks and a dataTable. The dataTable contains some commandLinks too. All commandLinks call a method of a JavaBean via AJAX.
And now the problem. If I click on the commandLinks link_edit or link_cancel the panelGroup is getting rerenderd. But if I click on a commadLink inside the dataTable like link_delete the panelGroup is not getting rerendered. Does anybody know what I'm doing wrong?
Here is the relevant section of the file:
<h:panelGroup
id="panel_destinations">
<h:commandLink
id="link_edit"
value="Edit"
styleClass="link"
rendered="#{not editRoute.inEditDestinationMode}">
<f:ajax
listener="#{editRoute.editDestinations()}"
render="panel_destinations"
immediate="true"/>
</h:commandLink>
<h:commandLink
id="link_cancel"
value="Abbrechen"
styleClass="link"
rendered="#{editRoute.inEditDestinationMode}">
<f:ajax
listener="#{editRoute.cancelEditDestinations()}"
immediate="true"
render="panel_destinations"/>
</h:commandLink>
<h:dataTable
value="#{editRoute.route.destinations}"
var="destination"
styleClass="dest_table"
headerClass="route_table_header"
rowClasses="route_table_row_odd,route_table_row_even"
rendered="#{editRoute.hasDestinations()}"
id="table_destinations">
<h:column>
<h:commandLink
styleClass="route_table_link"
value="Delete"
id="link_delete"
rendered="#{editRoute.inEditDestinationMode}" >
<f:ajax
listener="#{editRoute.deleteDestination(destination)}"
render="panel_destinations"/>
</h:commandLink>
</h:column>
</h:dataTable>
</h:panelGroup>

JSF and AJAX: hiding part of site until first ajax request

I'm trying to create a page where some content will be displayed after ajax request. Here is part of my code:
<h:panelGroup>
<h:form>
Retrive object by id: <h:inputText id="myInput" value="#{myManager.id}"/>
<h:commandButton value="ok" action="#{myManager.getById}" >
<f:ajax execute="myInput" render="resultRow" />
</h:commandButton>
<h:panelGroup id="resultRow" >
<br />
You retrived object which id is:
<h:outputText value="#{retrivedObject.id}" />
and its name is:
<h:outputText value="#{retrivedObject.name}" />
</h:panelGroup>
</h:form>
</h:panelGroup>
My problem is taht the "You retrived object which id is: " is rendered even before I retrive any object. I want it to be hidden until I click my command button. Is it possible to do only with jsf + html (this have to be done with ajax)? I could use some javascript if I will have to, but I prefer not.
I tried to solve this by rendered="#{!retrivedObject.id==0}" (my object can't have id 0), but this doesn't work - panel group isn't rendered at all and when I call ajax reqest it is unable to find "resultRow" id.
Thanks for help in advance.
It may not be exactly what you are looking for but you can always wrap the panelGroup in a div and set the div visibility to hidden, then use javascript to change the visibility when you click the command button.
You can use the rendered attribute like this :
<h:panelGroup>
<h:form>
Retrive object by id: <h:inputText id="myInput" value="#{myManager.id}"/>
<h:commandButton value="ok" action="#{myManager.getById}" >
<f:ajax execute="myInput" render="resultRow" />
</h:commandButton>
<h:panelGroup id="resultRow">
<h:panelGroup rendered="#{retrivedObject ne null}">
<br />
You retrived object which id is:
<h:outputText value="#{retrivedObject.id}" />
and its name is:
<h:outputText value="#{retrivedObject.name}" />
</h:panelGroup>
</h:panelGroup>
</h:form>
</h:panelGroup>

Escape a primefaces/jsf page that has required fields

I have a jsf page with
<h:inputText required="true"> attribute.
When I click on my add button, I want to see an error if the field is blank.
I have a <f:message> to display this.
But how can I escape (cancel) the page with that field blank? In my case it continually posts the error message and I have to enter something in the field to go to another page.
If your cancel button is a POST requests e.g. <p:commandButton>,<h:commandButton>, <p:commandLink>, or <h:commandLink>
You can put your cancel button outside the form that has the <h:inputText> so that form is not submitted
<h:form>
<h:inputText value="#{bean.string}" required="true"/>
</h:form>
<h:form>
<p:commandButton value="Cancel" action="anotherpage.xhtml"/>
</h:form>
If you are navigating without any actions or method invocation, then you can use GET requests e.g. <h:button>, <p:button>, <h:link> or <h:outputLink>.
Example:
<h:form>
<h:inputText value="#{bean.string}" required="true"/>
<p:button value="Cancel" outcome="anotherpage.xhtml"/>
</h:form>
You can use immediate="true" in your <p:commandButton>. Then it will skip the validation.
For example:
<p:commandButton action="#{managedBean.action}" value="Cancel" immediate="true" />
Just don't process the form on submit if you want to cancel or navigate away.
I.e. don't do
<h:form>
...
<p:commandButton value="Cancel" action="#{bean.cancel}" />
</h:form>
But just do
<h:form>
...
<p:commandButton value="Cancel" action="#{bean.cancel}" process="#this" />
</h:form>
Or, better if you're only returning a navigation outcome in cancel() method anyway
<h:form>
...
<p:button value="Cancel" outcome="otherpage.xhtml" />
</h:form>

Inputtext not syncing with backend bean when commandbutton is pushed

When I hit the commandButton it puts in the value that product.amount started as rather than what is currently typed in the input box.
<h:inputText value="#{product.amount}" />
<h:commandButton id="Button"
value="Buy"
tabindex="2" >
<f:ajax listener="#{shopBean.addToCart(product.product, product.amount)}"
execute="#this"
render="#all" />
</h:commandButton>
You need to include the input field in the execute attribute of the <f:ajax> so that it get processed as well, otherwise it will simply be ignored altogether.
<h:inputText id="amount" ... />
<h:commandButton ...>
<f:ajax execute="#this amount" ... />
</h:commandButton>
Or just put the whole in a single form and use execute="#form".
<h:form>
<h:inputText ... />
<h:commandButton ...>
<f:ajax execute="#form" ... />
</h:commandButton>
</h:form>
By the way, the render="#all" defeats one of the main advantages of using ajax. Try to render exactly only the component(s) which actually needs to be updated.

Rendering other form by ajax causes its view state to be lost, how do I add this back?

I have
<h:form>
<h:commandLink action="#{my_fake_ajax_link}">
<h:outputText value="Link" />
<f:ajax render=":mydiv" />
</h:commandLink>
</h:form>
<h:panelGroup layout="block" id="mydiv">
<h:form>
<h:commandLink action="#{mybean.delete(0)}">
<h:outputText value="Here" />
<f:ajax render="#form" />
</h:commandLink>
</h:form>
</h:panelGroup>
When I click once on "my_fake_ajax_link", then I have to click twice on "delete" link. This is only an example. I don't have this real case. I have multiple forms on a page and I can't just add all of them in a single form.
I checked what the problem is and it is:
When you click on "my_fake_ajax_link", the mydiv refreshs with ajax as it should.
ViewState of the refreshed form on ajax is missing.
How can I add the ViewState? How can I make it work without using only one form? This looks like an JSF bug to me. I don't want to refresh that div automatically with
jQuery("#mydiv").load(document.location.href);
but I will in my worst case possible.
This is a known problem in the auto-included jsf.js library of JSF which handles ajax responses. See also JSF spec issue 790 which is fixed in the upcoming JSF 2.3. In the meanwhile, with JSF 2.0/2.1/2.2, you have to explicitly specify the ID of the other <h:form> inside the render attribtue to trigger proper addition of the view state.
<h:form>
<h:commandLink action="#{my_fake_ajax_link}">
<h:outputText value="Link" />
<f:ajax render=":mydiv :mydivForm" />
</h:commandLink>
</h:form>
<h:panelGroup layout="block" id="mydiv">
<h:form id="mydivForm">
<h:commandLink action="#{mybean.delete(0)}">
<h:outputText value="Here" />
<f:ajax render="#form" />
</h:commandLink>
</h:form>
</h:panelGroup>
No, this does not cause any overhead or markup duplication in the ajax response. Alternatively, use OmniFaces fixviewstate.js.
See also:
Communication in JSF 2.0 - Ajax rendering of content which contains another form
h:commandButton/h:commandLink does not work on first click, works only on second click
Workaround for that:
First you need to set an onevent handler for the button that sends the ajax request:
<h:form id="newComment">
<h:commandButton id="saveNewComment" action="#{postBean.actionSaveNewCommentAjax}" value="#{rb['speichern']}">
<f:ajax execute="#form" render=":commentsBox" onevent="function(data) { fixOtherFormsViewState(data, 'newComment') }" />
</h:commandButton>
</h:form>
Then you need to declare this javascript methods that will take the correct viewState value and add it to all forms which doesn't have it:
<h:outputScript>
function fixOtherFormsViewState(data, goodFormId) {
if (data.status != "success") {
return;
}
var viewState = jQuery("#" + goodFormId + " input[name='javax.faces.ViewState']").val();
jQuery("form:not(:contains(input[name='javax.faces.ViewState']))").each(function (idx, elem) {
var form = jQuery(elem);
var input = jQuery("<input type='hidden' name='javax.faces.ViewState' />").val(viewState);
form.append(input);
});
}
</h:outputScript>

Resources