I am using PrimeFaces 5.1 in my application,I used selectOneMenu inside of dataTable, When I press on add more button to need to focus selectOneMenu button.
Sample Code
JavaScript
function focusMenu(id)
{
var focusId="#{p:component('mainTable')}"+":"+id;
PrimeFaces.focus(final);
}
xhtml
<div style="padding-left:4px;height:
expression(this.scrollHeight > 399? '400px' : 'auto');max-height:400px;overflow-x:auto;overflow-y:auto;">
<p:dataTable id="mainTable" value=#{main.UserDataTable} .....>
<p:column headerText="Drop Down">
<p:selectOneMenu id="dropdDownId"...>
.....
</p:selectOneMenu>
</p:column>
<p:column headerText="Operation">
<p:commandButton value="Add More Row" action="{user.addMoreRowAction}"
update="mainTable"/>
</p:column>
</p:dataTable>
</div>
Java
public String addMoreRowAction()
{
RequestContext.getCurrentInstance().execute("focusMenu('"+ rowIndex + ":dropdDownId"')");
return null;
}
Here the above code is work fine to focus the last added selectOneMenu as what I expected, but the browser scroll bar is not move down based on focusing the recently added selectOneMenu component.
Please help me to solve the issue.
You can take the scrollbar to the focus id using scrollTop function.
Pass the focusid into the function.
function setScrollPos(focusId){
$('html,body').animate({
scrollTop: $(focusId).offset().top-65
}, 1000);
}
Call the above function
function focusMenu(id)
{
var focusId="#{p:component('mainTable')}"+":"+id;
PrimeFaces.focus(final);
setScrollPos(focusId); <<------
}
Related
So I'm trying to nest a datatable within another datatable in the rowexpansion tag as follows:
<h:form>
<p:dataTable var="item"
value="#{bean.items}">
<p:column>
<p:rowToggler/>
</p:column>
<p:column headerText="Item Name">
<h:outputText value="#{item.name}"/>
</p:column>
<p:rowExpansion>
<p:dataTable var="subitem"
value="#{item.subitems}">
<p:column headerText="Subitem Name">
<h:outputText value="#{subitem.name}" />
</p:column>
<p:column>
<p:splitButton value="Save" action="#{bean.save}">
<p:menuitem value="Update" action="#{bean.update}"/>
<p:menuitem value="Delete" action="#{bean.delete}"/>
</p:splitButton>
</p:column>
</p:dataTable>
</p:rowExpansion>
</p:dataTable>
</h:form>
the testing methods in the bean are just
public void save() {
addMessage(FacesMessage.SEVERITY_INFO, "Success!", "Saved subitem");
PrimeFaces.current().ajax().update(":frmTopbar");
}
public void update() {
addMessage(FacesMessage.SEVERITY_INFO, "Success!", "Updated subitem");
PrimeFaces.current().ajax().update(":frmTopbar");
}
public void delete() {
addMessage(FacesMessage.SEVERITY_INFO, "Success!", "Deleted subitem");
PrimeFaces.current().ajax().update(":frmTopbar");
}
public void addMessage(FacesMessage.Severity severity, String summary, String detail) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(severity, summary, detail));
}
If only a single item and its subitems is loaded, everything displays and splitbutton buttons work fine.
The bean just sends a FacesMessage to test and like I said, the message is properly shown for each of the 3 buttons.
But as soon as I load 2 or more items and their subitems, everything looks visually correct, but the commands in splitbutton no longer reach bean, so no FacesMessage is sent.
Does anyone have a clue about what is going on or a suggestion as to what can be done to fix it?
I'm using primefaces v11.0
Thanks a lot!
I seem to have found a workaround with p:remoteCommand using BalusC's tips.
So what I've done, is place this somewhere in my page that will map a javascript function to a bean method.
<p:remoteCommand name="splitButtonMenuitemClicked" actionListener="#{bean.save}" style="display: none;" />
I then place this jquery code in my page to catch the click event from any split button:
jQuery( document).delegate( ".ui-splitbuttonmenu .ui-menuitem-link", "click",
function(e){
var menuitemText = $(this).find("span.ui-menuitem-text");
var itemText = $(menuitemText).text();
splitButtonMenuitemClicked([{ name: "fooName", value: itemText }])
}
);
Now in my bean function, I just retrieve my parameter as follows:
public void save() {
String fooName= FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("fooName");
//fooName is unique key in may table, so it works well to work on the correct entity
...
}
It'll do for now, although it would be nice if the components worked as one hopes.
Prerequisites:
JSF 2.1
Primefaces 5.2
Glassfish 3.1
Story:
I got a datatable displaying some editable values.
The amount of columns is dynamic.
In the same form is a button calling a save-action of my bean to save all edited data.
The function it self is working perfectly fine
Implementation:
<p:dataTable id="dataTable" scrollable="true"
widgetVar="wigVardataTable"
value="#{myBean.dataList}"
var="data"
editable="true"
editMode="cell"
rowKey="rowkey">
<p:columns var="col"
value="#{myModel.columnList}"
style="font-size: 12px">
<f:facet name="header">
<h:outputText
value="#{col.header}" />
</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText
value="#{myModel.getValueForTableCell(data, col).value}" />
</f:facet>
<f:facet name="input">
<p:inputText
id="inputTxt"
value="#{myModel.getValueForTableCell(data, col).value}"
style="width:100%">
</p:inputText>
</f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>
Problem:
When pressing the save button while a table cell is being edited and didn't loose its focus yet, the new value inside the inputtext of the tablecell won't be written back into my bean and due to that won't be saved.
Question:
How can i write back the data into my backing bean, before the button action is executed?
This behavior is caused by wrong ordering of ajax events. When the command button is invoked while having a cell editor open, then the ajax event of the command button is fired before the ajax event of the cell edit.
We'd thus like to swap around them. Without editing the PrimeFaces source code, you could achieve this by explicitly invoking saveCell() function of the <p:dataTable> widget when there's a cell editor open.
So, given a
<h:form>
<p:dataTable ... widgetVar="wigVardataTable" />
<p:commandButton ... />
</h:form>
you can achieve the desired behavior by adding the below onclick to the <p:commandButton>:
onclick="var d=PF('wigVardataTable'), c=d.jq.find('td:has(.ui-cell-editor-input:visible)'); if(c.length)d.saveCell(c)"
Basically, it first finds the cell with an open cell editor and then invokes saveCell() on it.
Another, less elegant, way is explicitly invoking showCellEditor() function of the <p:dataTable> widget without passing the expected target cell in a try-catch block where the exception is ignored.
onclick="try{PF('wigVardataTable').showCellEditor()}catch(ignore){}"
It will save any opened cell first and later throw an error that no new target cell was specified as argument, but that could be ignored as shown above.
Noted should be that this all works regardless of whether you use <p:columns> or <p:column>, and also regardless of whether you use a normal model or an overcomplicated model ;) This trick is tested on both your PrimeFaces 5.2 version and the current PrimeFaces 6.0 version.
Here is a generic solution inspired by BalusC answer if you have other datatables with cell editMode :
<script type="application/javascript">
function saveAllCellsBeforeSubmit() {
for (var widgetName in PrimeFaces.widgets) {
if (PrimeFaces.widgets[widgetName].hasOwnProperty('cfg') && PrimeFaces.widgets[widgetName].cfg.hasOwnProperty('editMode') && PrimeFaces.widgets[widgetName].cfg.editMode === 'cell') {
var d = PF(widgetName);
var c = d.jq.find('td:has(.ui-cell-editor-input:visible)');
if (c.length) {
d.saveCell(c);
}
}
}
}
</script>
In normal circumstances like this:
<h:form>
<h:selectOneRadio value="#{bean.gender}">
<f:selectItem itemValue="Male" itemLabel="Male" />
<f:selectItem itemValue="Female" itemLabel="Female" />
<f:selectItem itemValue="Other" itemLabel="Other" />
</h:selectOneRadio>
<h:commandButton value="Submit" action="#{bean.action}" />
</h:form>
Selecting one radio button disselects the other & the radio button will be remain selected on the postback. (when the same view is rendered)
However, when we're dealing with an iterating component like <h:dataTable>, the selection is lost.
Consider the snippet:
<h:form id="hashMapFormId">
<b>HASH MAP:</b>
<h:dataTable value="#{playersBean.dataHashMap.entrySet()}" var="t" border="1">
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectOneRadio id="radiosId" onclick="deselectRadios(this.id);"
value="#{playersBean.select}">
<f:selectItem itemValue="null"/>
</h:selectOneRadio>
</h:column>
</h:dataTable>
<h:commandButton value="Show Hash Map Selection"
action="#{playersBean.showSelectedPlayer()}" />
</h:form>
With disselecting the other radio buttons when one radio button is selected being implemented by simple JavaScript-
function deselectRadios(id) {
var f = document.getElementById("hashMapFormId");
for (var i = 0; i < f.length; i++)
{
var e = f.elements[i];
var eid = e.id;
if (eid.indexOf("radiosId") !== -1) {
if (eid.indexOf(id) === -1) {
e.checked = false;
} else {
e.checked = true;
}
}
}
}
Fire the GET request:
Select a radio button:
Now press the submit button, response:
You see that the radio button gets dis selected on postback. How to solve this shortcoming?
I know it very well that this is due to this component attribute itemValue being null:
<f:selectItem itemValue="null"/>
This trick is a leftover from JSF 1.x / 2.0/2.1 when it wasn't possible to use a <h:selectOneRadio> for single row selection in a <h:dataTable>. This trick originated in my 10 year old blog article Using Datatables - Select row by radio button.
The root problem is, HTML radio buttons are grouped based on their name attribute, so the webbrowser knows which others to unselect when one is selected. But JSF generates by design a different one for each <h:dataTable> item, with the row index inlined and therefore they can't be grouped and hence the JavaScript based workaround.
Since JSF 2.2, with the new passthrough elements and attributes feature, it's however possible to force the name attribute to the value of your choice and capture the selected item via a helper <h:inputHidden>. This is fleshed out in another blog article of me, from previous year: Custom layout with h:selectOneRadio in JSF 2.2. The article uses <ui:repeat> as an example, this can be rewritten to <h:dataTable> as below.
<h:form>
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
value="#{item.id}" a:checked="#{item.id eq bean.selectedItemId ? 'checked' : null}" />
</h:column>
<h:column>#{item.id}</h:column>
<h:column>#{item.name}</h:column>
</h:dataTable>
<h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItemId}"
rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>
#Named
#ViewScoped
public class Bean implements Serializable {
private List<Item> items;
private Long selectedItemId;
// ...
public void submit() {
System.out.println("Selected item ID: " + selectedItemId);
}
// ...
}
And yes, the selected radio button remains selected on postback this way. You can also pass whole entities, this only requires a converter on the <h:inputHidden>.
I'm using primefaces to show a datable :
I have a column in my datable , it's an image , what i want is when i clic in the image , a p:dialog show the image with a big dimension .
I did it but the p:dialog show the wrong image, it shows the image of the last row in the datable .
<p:column headerText="imagePath">
<h:graphicImage library="images" name="#{book.imagePath}.png"
width="75px" onclick="PF('dlg').show();"/>
<p:dialog widgetVar="dlg" resizable="f" height="600px"width="500px"onShow="js">
<h:graphicImage library="images" name="#{book.imagePath}.png"width="400px"/>
</p:dialog>
</p:column>
Thank you
I have solved the problem with the commandlink component
<p:column headerText="imagePath">
<p:commandLink update=":zaki:foo:imgDlg1" oncomplete="dlg1.show()"
action="#{jsfClient.showImg(book)}">
<h:graphicImage value="/resources/images/#{book.imagePath}.png" width="75px"/>
</p:commandLink>
</p:column>
I have put in image into a commandlink and it works perfect , note that i have put the p:dialog out from the datable.
You should poll the dialog out of the dataTable and record which book did the user select before open the dialog.
Take a look at this code:
<p:dataTable value="#{imgBean.books}" var="book">
<p:column headerText="imagePath">
<h:commandButton image="/images/#{book.imagePath}.png" style="width: 75px" action="#{imgBean.showImg(book)}"/>
</p:column>
</p:dataTable>
<p:dialog widgetVar="dlg" id="imgDlg" resizable="f" height="600px" width="500px">
<h:graphicImage value="../images/#{imgBean.selectedBook.imagePath}.png" width="400px"/>
</p:dialog>
The action of the commandButton looks like:
private Book SelectedBook;
public void showImg(Book book){
this.SelectedBook=book;
RequestContext.getCurrentInstance().update("imgForm:imgDlg");
RequestContext.getCurrentInstance().execute("PF('dlg').show()");
}
public Book getSelectedBook() {
return SelectedBook;
}
In this way, you can not only show the image but also other information of the book.
BTW, you can also update/show the dialog in the h:commandButton instead of from backing Bean.
This is common widgetvar conflict. Think about how many p:dialogs are rendered in datatable and what is widgetvar of each? Inspect your rendered page to get better idea.
To resolve this issue either use some image id and pass it to custom javascript or create property (ex. "selectedBookImage") in your baking bean and refer it from <p:dialog>
. Using this technique you can easily move p:dialog outside datatable.
I have a tabview in which I want to refresh contents of one specific tab whenever user selects that tab. I also want modal dialog to pop up while tab is being refreshed.
Here is tabView with tabChange ajax event handler
<p:dialog widgetVar="statusDialog" modal="true" draggable="false" minimizable="false" appendToBody="true" closable="false" header="Processing..." resizable="false" maximizable="false">
<p:graphicImage library="assets" name="ajax-loader.gif"></p:graphicImage>
</p:dialog>
<p:tabView id="tabview" orientation="top" dynamic="false">
<p:ajax event="tabChange" listener="#{bean.tabChangeListener}"></p:ajax>
<p:tab title="tab1">
<ui:include src="/WEB-INF/views/tab1.xhtml"/>
</p:tab>
<p:tab title="tab2">
<p:remoteCommand actionListener="#{bean.refreshData}" update="someTab2ElementID" name="refresh" global="true" onstart="statusDialog.show()" oncomplete="statusDialog.hide()"/>
<ui:include src="/WEB-INF/views/tab2.xhtml"/>
</p:tab>
</p:tabView>
Here is tabChangeListener:
public void tabChangeListener(TabChangeEvent event) {
if ( event.getTab().getId().equalsIgnoreCase("tab2") ) {
RequestContext.getCurrentInstance().execute("refresh()");
}
}
The refresh remoteCommand is being called as expected, but my statusDialog is never shown. If the same remoteCommand is triggered by a button click the statusDialog appears. There are no errors in JavaScript console.
Why is statusDialog not shown when remoteCommand is triggered by RequestContext.execute() and how can I make it appear? I even tried adding statusDialog.show() to execute() but it didnt help.
I figured it out. Here is the solution:
<h:outputScript>
var blockCount=0;
function showStatus() {
if (blockCount==0) statusDialog.show();
blockCount++;
};
function hideStatus() {
blockCount--;
if (blockCount==0) statusDialog.hide();
};
</h:outputScript>
<p:ajaxStatus onstart="showStatus();" onsuccess="hideStatus();" onerror="blockCount=0;statusDialog.hide();errorDialog.show();"/>
<p:remoteCommand actionListener="#{bean.refreshData}" update="someTab2ElementID" name="refresh" global="true" onstart="showStatus()" oncomplete="hideStatus()"/>
Apparently the reason why my status dialog was not being shown is that another Ajax call finished and called statusDialog.hide() while my refresh() thing was going on. The JS code above maintains a counter of how many Ajax calls are in progress, and will only hide the status dialog when all calls have finished. Works as intended now!
If you want to show dialog when a specific tab is changed, you can inform a js function about, the tab selected control that it is 2 or not by adding a callback parameter via ajax response:
public void tabChangeListener(TabChangeEvent event) {
if ( event.getTab().getId().equalsIgnoreCase("tab2") ) {
RequestContext.getCurrentInstance().addCallbackParam("index", 2);
}
}
And use:
<p:ajax event="tabChange" listener="#{bean.tabChangeListener}"
oncomplete="showOrNot(xhr,status,args)"></p:ajax>
This will call the js function and it's going to control that the changed tab is 2 or not:
function showOrNot(xhr,status,args) {
if(args.index==2) {
statusDialog.show();
}
}
Or different approach is defining a value and using visible property of the p:dialog:
public void tabChangeListener(TabChangeEvent event) {
if ( event.getTab().getId().equalsIgnoreCase("tab2") ) {
this.condition=true;
}
}
And <p:dialog visible="#{bean.condition}"/>. And maybe you need to focus on your first approach and you can give an ID to p:dialog and update it via p:remoteCommand:
<p:remoteCommand actionListener="#{bean.refreshData}" update="someTab2ElementID dialog" name="refresh" global="true" onstart="statusDialog.show()" oncomplete="statusDialog.hide()"/>
Don't forget to give the exact client ID of the dialog in the update property of p:remoteCommand.