How to update a specific cell on a PrimeFaces dataTable - jsf

In my dataTable I am linking each article with a specific task.
On the click of a commandButton a list of tasks shows up, so I want on the select of a task, update a specific cell in the dataTable (outputText with id="columnTache") without updating the rest of my dataTable.
<p:dataTable value="#{myController.articleList}"
id="tabArticle"
var="article"
rowKey="#{article.id}" >
<p:column headerText="quantite" >
<pe:inputNumber value="#{article.quantite}" />
</p:column>
<p:column headerText="taches" >
<h:outputText value="#{article.tache.libelleTache}" id="columnTache" />
</p:column>
<p:column headerText="taches" >
<p:commandButton oncomplete="PF('dialogTasks').show();" update=":formSelectAffecterTache">
<p:ajax event="click" listener="#{myController.setArticle(article)}" />
</p:commandButton>
</p:column>
</p:dataTable>
<p:dialog header="#{bundleTech.lbl_taches}" widgetVar="dialogTasks" >
<h:panelGrid columns="1" >
<h:form id="formSelectAffecterTache">
<ui:include src="/pages/listTacheSelect.xhtml">
<ui:param name="bean" value="#{myController}" />
<ui:param name="action" value="affecterTache" />
</ui:include>
</h:form>
</h:panelGrid>
</p:dialog>
The update of my dataTable is in the managed bean:
public void affecterTache() {
article.setTache(selectedSingleTache);
RequestContext.getCurrentInstance().update("form:tabArticle");
}

A dataTable is a naming container, so components within it will be prepended with the dataTable's id. Also, each iteration of data presented in the table (each row) will have effect of the generated ids. Since a form is also a naming container, your outputText component ids will be generated as formId:tabArticle:0:columnTache in the first row, formId:tabArticle:0:columnTache in the second row, etc.
If you would set an id on your commandButton you will get formId:tabArticle:0:myButton, formId:tabArticle:1:myButton etc. You can use this id in your click handler to create the corresponding inputText clientId. But, since you pass article to a method, you are not able to check which button was clicked. So first of all change the click listeners method to:
public void useArticle(AjaxBehaviorEvent event) {
UIComponent component = event.getComponent();
}
Now, you know which button was clicked (event.getComponent()), but you need your article as well. You can set that as an attribute to your button in XHTML:
<p:commandButton id="myButton"
oncomplete="PF('dialogTasks').show();"
update=":formSelectAffecterTache">
<p:ajax event="click"
listener="#{myController.useArticle}" />
<f:attribute name="article"
value="#{article}" />
</p:commandButton>
This attribute can be read in your listener:
Article article = (Article) component.getAttributes().get("article");
Now, for updating the inputText, simply use the button's clientId:
String update = component.getClientId().replace(":myButton", ":columnTache");
Store it in your bean, and when you are ready to do so, update:
RequestContext.getCurrentInstance().update(update);
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Send additional parameter to Ajax event listener

Related

Blocking a p:commandButton displayed on a p:confirmDialog in addition to displaying a dynamic message on the dialog

I'm trying to block a <p:commandButton> which is displayed on a <p:confirmDialog> as follows.
<p:confirmDialog id="confirmDeleteDialog" widgetVar="confirmDelete" message="Message" closeOnEscape="true" appendTo="#(body)" closable="true">
<p:blockUI block="confirm" widgetVar="blockUI">
<h:outputText value="Demo"/>
</p:blockUI>
<p:commandButton id="confirm" value="Yes" onstart="PF('blockUI').show()" oncomplete="PF('blockUI').hide();"/> <!--Use PF('confirmDelete').hide() to dismiss the dialog.-->
<p:commandButton id="decline" value="No" onclick="PF('confirmDelete').hide()" type="button" />
</p:confirmDialog>
<p:commandButton oncomplete="PF('confirmDelete').show()"/>
This blocks Yes button (one with the id="confirm") held by <p:confirmDialog>.
The message to be displayed on the confirm dialog is dynamic. It is fetched from the associated backing bean based on certain conditions. Therefore, the dialog is required to be updated before it is displayed.
To update the dialog before it is displayed, the update attribute of <p:commandButton> is set as follows.
<p:commandButton oncomplete="PF('confirmDelete').show()" update="confirmDeleteDialog"/>
This of course, displays a desired message on the dialog dynamically (which is not demonstrated here for the sake of simplicity) but doing so prevents the <p:blockUI> from functioning - it does not block the <p:commandButton>, if the dialog is updated by it.
Neither errors on the browser console nor exceptions on the server terminal are seen.
What is the way of blocking a button held by a in addition to displaying a dynamic message on the dialog?
EDIT :- the answer provided by me is cross-mark.
As noted in the answer given by me, <p/pe:blockUI> needs to be updated, when the <p:commandButton> held by the confirm dialog is clicked, it takes some time to block the button - <p:commandButton> after it is clicked. In the meanwhile, the button can be clicked (either deliberately or accidently) before the initial request completes that may cause duplicate submits which is certainly against the use of <p/pe:blockUI>.
Nothing is preventing a user in my workaround, from clicking the button multiple times before it is blocked by <p/pe:blockUI>. Therefore, the answer provided by me is considered to be cross-mark.
Perhaps the way to meet this requirement is completely different.
EDIT 2:- the actual scenario.
//A view scoped bean.
//A list of selected rows in <p:dataTable>.
private List<WishUtils>selectedValues;
//The actual message to be displayed on the dialog.
private String deleteMsg;
//Associated with the rendered property of the "Yes" button on the dialog.
private boolean renderedYesButtonDelete=true;
//The header message/text of the dialog.
private String messageBoxHeader;
//The no button text (its value).
private String noButtonTextDelete="No";
//Getters and setters as required.
public void confirmDelete(ActionEvent actionEvent) {
if(selectedValues!=null && !selectedValues.isEmpty()) {
renderedYesButtonDelete=true;
noButtonTextDelete="No";
deleteMsg="A long message about a delete prompt from a resource bundle";
messageBoxHeader="Confirm Delete";
} else {
noButtonTextDelete="OK";
renderedYesButtonDelete=false;
deleteMsg="Please select the rows you want to delete";
messageBoxHeader="Confirm Item Select";
}
}
public void delete(ActionEvent actionEvent) {
if(actionEvent.getComponent().getId().equals("confirmDeleteMultiple")) {
//Delete the selected rows.
} else {
//Notify an error (generally never going to be executed).
}
}
The message box goes here :
<p:confirmDialog id="confirmDialogDeleteMultiple" widgetVar="confirmDeleteUIWidget" header="#{wishManagedBean.messageBoxHeader}" closeOnEscape="true" appendTo="#(body)" closable="true">
<p:blockUI id="blockDelete" block="confirmDeleteMultiple" widgetVar="blockDeleteUIWidget"/>
<p:remoteCommand name="blockDeleteCommand" update="blockDelete" process="#this"/>
<p:commandButton id="confirmDeleteMultiple"
value="#{messages['confirmdialog.yes']}"
onclick="blockDeleteCommand();"
onstart="PF('blockDeleteUIWidget').show();"
oncomplete="PF('blockDeleteUIWidget').hide();PF('confirmDeleteUIWidget').hide()"
actionListener="#{wishManagedBean.delete}"
process="#this"
rendered="#{wishManagedBean.renderedYesButtonDelete}"
update="a list of components to be updated"/>
<p:commandButton id="declineDeleteMultiple"
value="#{wishManagedBean.noButtonTextDelete}"
onclick="PF('confirmDeleteUIWidget').hide()"
type="button"/>
<f:facet name="message">
<p:outputPanel>
<h:outputFormat value="#{wishManagedBean.deleteMsg}" escape="false"/>
</p:outputPanel>
</f:facet>
</p:confirmDialog>
The <p:blockUI> indeed fails when the entire dialog is updated. Looks like just another bug. It works when you explicitly call the PrimeFaces.cw(...) "create widget" script of the <p:blockUI> component (exactly that script which you see in generated HTML output representation of <p:blockUI>) on complete of the dialog update.
Given those IDs,
<h:form id="formId">
<p:confirmDialog id="confirmDialogId">
<p:blockUI id="blockId" ... />
<p:commandButton id="confirmButtonId" ... />
The following oncomplete should do it:
<p:commandButton update="confirmDialogId"
oncomplete="PrimeFaces.cw('BlockUI','blockUI',{id:'formId:blockId',block:'formId:confirmId'});PF('confirmDialogId').show()" />
That explains why the <p:remoteCommand> trick works as it under the covers basically re-generates the <p:blockUI> component along with that PrimeFaces.cw(...) call, although it fires an unnecessary ajax request. It might be worth reporting an issue to PrimeFaces guys, telling that PrimeFaces.cw(...) of <p:blockUI> isn't properly executed when the confirm dialog widget is updated.
Another workaround would be to explicitly ajax-update only the parts you'd like to update instead of the entire dialog. That works for me. The header and the message attributes of the <p:confirmDialog> (and those of many other PrimeFaces components) supports being defined via <f:facet> on the attribute name. This allows you to wrap it in a <h:outputText> (or <h:panelGroup>) so that you can just update it individually. This way you don't need to update the entire dialog and the block UI keeps working as intented.
<p:confirmDialog ...>
<f:facet name="header">
<h:outputText id="confirmDeleteDialogHeader" value="#{bean.header}" />
</f:facet>
<f:facet name="message">
<h:outputText id="confirmDeleteDialogMessage" value="#{bean.message}" />
</f:facet>
<h:panelGroup id="confirmDeleteDialogContent">
<p:blockUI ... />
<p:commandButton ... />
<p:commandButton ... />
</h:panelGroup>
</p:confirmDialog>
<p:commandButton ...
update="confirmDeleteDialogHeader confirmDeleteDialogMessage confirmDeleteDialogContent"
oncomplete="PF('confirmDelete').show()" />
This requires the <p/pe:blockUI> to be updated, when the <p:commandButton> displayed on the confirm dialog is clicked.
The <p:blockUI> can be updated onclick using the <p:remoteCommand> (onstart also works).
The code goes here.
<p:confirmDialog id="confirmDeleteDialog" widgetVar="confirmDelete" message="Message" closeOnEscape="true" appendTo="#(body)" closable="true">
<p:blockUI id="blockConfirm" block="confirm" widgetVar="blockUI"/>
<p:remoteCommand name="confirmCommand" update="blockConfirm"/>
<!--Add PF('confirmDelete').hide() to oncomplete to dismiss the dialog, when this button is clicked.-->
<p:commandButton id="confirm" value="Yes" onclick="confirmCommand();" onstart="PF('blockUI').show()" oncomplete="PF('blockUI').hide()"/>
<p:commandButton id="decline" value="No" onclick="PF('confirmDelete').hide()" type="button" />
</p:confirmDialog>
<p:commandButton oncomplete="PF('confirmDelete').show()" update="confirmDeleteDialog" value="Submit"/>

Capturing primefaces <p:autoComplete> change event (detect emptying)

i got a question concerning an Primefaces autocomplete. Rigth now i am updating a field with the value of a property of the selected value in the autocomplete, like this:
This is in my xhtml:
<p:autoComplete
value="#{trFitoModel.selectedProducte}"
id="nomComercial"
completeMethod="#{trFitoBacking.completeProducte}"
var="producte" itemLabel="#{producte.nom}"
itemValue="#{producte}" converter="#{ProducteFitoConverter}"
forceSelection="true"
onkeyup="this.value = this.value.toUpperCase();">
<p:ajax event="itemSelect"
listener="#{trFitoBacking.handleSelect}"
update="text" global="false" />
<f:validator validatorId="qea.validators.EmptyFieldValidator" />
<f:attribute name="validationTitle" value=" NomComercial " />
</p:autoComplete>
</p:column>
<p:column>
<h:outputLabel>#{msgI18N.trFito}</h:outputLabel>
<h:outputText id="text"
value="#{traFitoBacking.resgistre}">
</h:outputText>
</p:column>
And this is my Backing Bean:
public void handleSelect(SelectEvent event) {
ProducteFitosanitari p=(ProducteFitosanitari)event.getObject();
setResgistre(Integer.toString(p.getNumRegistre()));
}
This works, but now I'm trying to update the outputText with id "text" with an empty String when the value of the autocomplete is empty.
How can I capture the event triggered when p:autoComplete is emptied?
Primefaces autoComplete generates 2 events: "change" and "itemSelect", for 2 methods of changing its content: typing or selecting from dropdown list. So you need to register 2 p:ajax listeners:
<p:autoComplete ... >
<p:ajax event="itemSelect" listener="#{bean.action}" process="#form"/>
<p:ajax event="change" listener="#{bean.action}" process="#form"/>
</p:autoComplete>
You'll need also 2nd server method signature:
public void action(AjaxBehaviorEvent event)
for capturing 'change' event.
Instead of event you can use onstart attribute with JavaScript to run... More options for p:ajax you'll find in Primefaces Users Guide, section "AjaxBehavior".

JSF binding command button dynamically

I have a JSF page that dynamically builds tabs. In each tab there are some input fields and a command button. The fields and buttons are dynamically built when the tabs are built. When I add a binding attribute to the command button, only one button in one tab is generated. There should be a command button in every tab. When I remove the binding attribute all the buttons are there but no required validation occurs. How can I add a unique binding value for each command button created in the loop.
All of the below code is inside a loop that generates tabs in a tab view.
<ui:repeat value="#{cc.attrs.dataTableTemplate.columns}" var="column">
<p:outputLabel styleClass="outputLabel" value="#{column.heading}"
for="inputtext" rendered="#{column.editable}"/>
<p:inputText value="#{cc.attrs.dataTableTemplate.collectorValue[column.name]}"
rendered="#{column.editable}"
id="inputtext" required="#{not empty param[add.clientId]}"/>
<div class="clear"> </div>
</ui:repeat>
<p:commandButton styleClass="commandButton"
binding="#{add}"
value="add"
update="pnl_codetable #parent"
title="Add">
<p:collector value="#{cc.attrs.dataTableTemplate.collectorValue}"
addTo="#{cc.attrs.pageBean.getDataTableList(
cc.attrs.dataTableTemplate.tableListName)}" />
</p:commandButton>
Here is the code that creates the tabs and calls the above code with the maintenence_code tag.
<p:accordionPanel id="pnl_accord_codetables" dynamic="true" cache="false">
<p:ajax event="tabChange" listener="#{pc_Maintenence.onTabChange}"/>
<c:forEach items="#{pc_Maintenence.codeMaintenenceTables}" var="codetable">
<p:tab title="#{codetable.tableName}">
<util:maintenence_code pageBean="#{pc_Maintenence}" dataTableTemplate="#{codetable}"/>
</p:tab>
</c:forEach>
</p:accordionPanel>
The binding attribute is basically view scoped. You can't bind physically multiple components in the same view to one and same view scoped variable. It can refer only one component. In your case, basically every component is overriding each other during setting that attribute so that you end up seeing only one, which is the last one.
You're not clear on how you're creating tabs, but you should in some way include the tab identifier in the variable used in the binding attribute. Perhaps something like this?
faces-config.xml:
<managed-bean>
<managed-bean-name>components</managed-bean-name>
<managed-bean-class>java.util.HashMap</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
View:
<c:set var="buttonId" value="add_#{someTabId}" />
...
<p:inputText ... required="#{not empty param[components[buttonId].clientId]}" />
...
<p:commandButton binding="#{components[buttonId]}" ... />
Update, wait it's a composite, another way would be to just bind it to the composite itself
<cc:interface componentType="myComposite">
...
<p:inputText ... required="#{not empty param[cc.add.clientId]}" />
...
<p:commandButton binding="#{cc.add}" ... />
with
#FacesComponent("myComposite")
public class MyComposite extends UINamingContainer {
private UICommand add;
// +getter +setter
}

Primefaces DataTable - deleting with dialog deletes wrong entry

I have a table in a form. One of the columns in the table displays a row of buttons to edit and delete the table entries. When I delete an entry callign the controller from a button's action attribute, it works as expected.
But once I have added a dialog to let the user confirm the deletion, a wrong entry is deleted. It is always the last entry in the current table. I have no idea what the reason could be - I am using the same DataTable var for the button and for the dialog.
I am working with JSF 2 (Mojarra 2.1.6) and Primefaces 3.5 deploying on JBoss 7.1.1 on a Suse 12.2 machine.
The form:
<h:form id="downloads">
<ui:include src="components/table.xhtml"/>
</h:form>
The table:
<ui:composition>
<p:dataTable value="#{controller.currentLevelResources}"
var="download" id="downloadTable" scrollHeight="120" rows="10">
<p:column sortBy="#{download.name}">
<f:facet name="header">Name</f:facet>
<h:outputText id="downloadName" value="#{download.name}" title="#{download.description}" />
</p:column>
...
<p:column>
<ui:include src="menuBar.xhtml"></ui:include>
</p:column>
The menu bar:
<ui:composition>
<p:commandButton id="edit"
action="#{downloadEditController.editResource(download)}"
icon="ui-icon-gear" title="Edit" oncomplete="updateStyles()"
update=":downloads" />
<p:commandButton id="delete" onclick="deletedDlg.show();"
icon="ui-icon-trash" title="Delete" oncomplete="updateStyles()" />
<p:dialog header="Delete confirmation" widgetVar="deletedDlg"
resizable="false">
<h:panelGroup layout="block" style="padding:5px;">
<h:outputText
value="The Resource #{download} will be deleted. Proceed?" />
</h:panelGroup>
<p:commandButton id="deleteBtn" value="Delete"
oncomplete="deletedDlg.hide(); updateStyles(); "
action="#{downloadEditController.deleteResource(download)}"
process="#this" update=":downloads">
</p:commandButton>
<p:commandButton value="Cancel" type="button"
onclick="deletedDlg.hide();" />
</p:dialog>
If I replace the dialog with this, everything works:
<p:commandButton id="delete" icon="ui-icon-trash" title="Delete"
action="#{downloadEditController.deleteResource(download)}"
oncomplete="updateStyles()" update=":downloads" />
Creating a <p:dialog> for every row is not a good idea.
For starters you had better create a single <p:dialog> outside your <p:dataTable>.
Next thing that I would do is to set the id or the row var in your bean upon delete button click and in case of confirmation in dialog use that id or the row var from bean to delete.
This is how your delete button could look like, set the download var in prepareDataForDeletion action and show the dialog...
<p:commandButton id="deleteConfirmation" icon="ui-icon-trash" title="Delete"
action="#{downloadEditController.prepareDataForDeletion(download)}"
onsucess="deletedDlg.show();"/>
Regarding your current anomaly: it's because all your dialogs have the same widgetVar and each next one is overriding the previously declared one all the way until the last one. You could dynamically give them a different widget names like so widgetVar="deleteDlg_#{someIndex}", but this makes no sense if you can have just only one reusable dialog whose content is updated before opening.

JSF render is not working for form element

I have a composite displayed inside a dialog. I have an edit button that get the current bean #SessionScoped (item in a data table) and then update the UI. My app is very similar to a simple CRUD app like http://balusc.blogspot.com/2010/06/benefits-and-pitfalls-of-viewscoped.html.
The problem is that the UI is updated correctly when using <h:outputText/> but not when using a form element.
<h:inputTextarea value="#{cc.attrs.managedBean.assertionStatement}" />
<h:inputText value="#{cc.attrs.managedBean.assertionStatement}" />
<h:outputText value="#{cc.attrs.managedBean.assertionStatement}"/>
The UI shows an empty textarea and input but the outputText renders the correct value. The getAssertionStatement() is called 3 times which seems to be the correct behavior.
When I close the dialog and reopen it, everything (form element) is populated.
The dialog call (ag namespace is for composite component):
<p:dialog widgetVar="DataValueRuleDialog" modal="true" height="600" width="800">
<p:outputPanel id="DataValueRulePanel">
<ag:DataValueAssertion managedBean="#{dataValueAssertionController}" id="DataValueComposite" />
</p:outputPanel>
</p:dialog>
The composite that calls another composite:
<h:form id="DataValueForm">
<ag:assertionMetadataComponent
managedBean="#{cc.attrs.managedBean.dataValueAssertionBean.assertionMetadataBean}"
assertionStatementRows="5" />
<p:dataTable value="#{cc.attrs.managedBean.model}" var="item">
<p:column>
<f:facet name="header">Assertion Statement</f:facet>
<h:outputText rendered="#{item.profileBean.profileLocation == cc.attrs.managedBean.selectedComformanceProfile.name}" value="#{item.assertionMetadataBean.assertionStatement}" />
</p:column>
<p:column>
<p:commandButton rendered="#{item.profileBean.profileLocation == cc.attrs.managedBean.selectedComformanceProfile.name}" value="edit" immediate="true"
actionListener="#{cc.attrs.managedBean.editDataValueAssertion}" update=":DataValueComposite:DataValueForm">
</p:commandButton>
</p:column>
</p:dataTable>
</h:form>
When I remove the immediate=true the form is validated and since one of the required field (supposed to be populated) is missing, I got a validation error. This is why I have immediate=true but it should be necessary since all the items in the data table should be valid.

Resources