Combine Drag&Drop and Reorder in a p:dataTable - primefaces

I need to combine Drag and Drop and Reorder on a DataTable row. I know that there are ways to do this on different columns (like one column for the reorder, the other one for Drag and Drop), but I need to do it on one Element.
I tried different ways, but nothing worked. The Problem is, that once the reordering is triggered, I can't drop it in the area. It always returns to the end of the table.
Getting the mouse position wouldn't work, because i need to make it responsive and usable for mobile devices.
HTML:
<p:fieldset id="availableCarsField" legend="Available Cars">
<p:dataTable id="availableCars"
var="car"
widgetVar="widgetVarAvailableCars"
draggableRows="true"
value="#{dndCarsView.cars}">
<p:column id="dragColumn"style="width:20px;">
<h:outputText id="test" value="#{car.id}"/>
<p:draggable helper="clone" revert="true"/>
</p:column>
</p:dataTable>
</p:fieldset>
<!-- ........................... DROP ...........................-->
<p:fieldset id="selectedCars" legend="Selected Cars">
<p:outputPanel id="dropArea">
<h:outputText value="!!!Drop here!!!"
rendered="#{empty dndCarsView.droppedCars}"
style="font-size:24px;"/>
<p:dataTable id="selectedCarsTable"
var="car"
value="#{dndCarsView.droppedCars}"
rendered="#{not empty dndCarsView.droppedCars}">
<p:column headerText="Id">
<h:outputText value="#{car.id}"/>
</p:column>
</p:dataTable>
</p:outputPanel>
</p:fieldset>
<p:droppable for="selectedCars"
tolerance="touch"
activeStyleClass="ui-state-highlight"
onDrop="handleDrop"
datasource="availableCars">
<p:ajax listener="#{dndCarsView.onCarDrop}"
process="#form"
update="dropArea availableCars"/>
</p:droppable>
JS:
function handleDrop(event, ui) {
var droppedCar = ui.draggable;
droppedCar.fadeOut('fast');
}
Bean (in case it's required):
public class DNDCarsView implements Serializable {
private List<Car> cars;
private List<Car> droppedCars;
#PostConstruct
public void init() {
cars = createCars(9);
droppedCars = new ArrayList<>();
}
public List<Car> createCars(int size) {
List<Car> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(new Car("" + i, "Brand" + i, i * 2000, "Color" + i));
}
return list;
}
public void onCarDrop(DragDropEvent ddEvent) {
Object car = ddEvent.getData();
droppedCars.add((Car) car);
cars.remove(car);
}
public void onReorder(ReorderEvent event) {
Car car = cars.get(event.getFromIndex());
cars.add(event.getToIndex(), car);
cars.remove(event.getFromIndex());
}
//Getter & Setter
}
Is there anyway to combine these two and get this running?
Thanks in advance
Edit:
I want to reorder the rows inside the DataTable and also be able to drop the dragged row into the fieldset.
Primefaces 6.2.12
JSF 2.2
Edit 2:
After month of trying I haven't found a good solution for my problem. Just thought, that I might share this here, if someone else deals with this problem.
I seems like there is no way to realize Drag&Drop in combination with columnreorder on one element (in my case p:column). The problem is, that always the reorderevent is triggered when dragged. When dropped into the droparea, the dragId of the dragged element is null (because we started with the reorder-event but ended with the event for drag and drop)
Due to the fact that I wasn't allowed to change anything which wasn't my class I didn't manage to fix this. I ended up to separate the actions onto two elementes. So I made a drag handle where the user can drag the element into the droparea and realized the reorder on the column.
If I was blind and someone knows an answer to this feel free to write. Maybe it might help someone. At least it would be interesting whether there is a way to do so

Related

p:selectmanycheckbox in p:datatable overrides selections

The first pic shows the selection of two checkboxes in a datatable. There were two rows of the datatable shown. At the first checkbox of the row you can see the brackets of the selection list. The only action were the selection of the checkboxes. Nothing more.
Now, when I reload the page or click on the button at the botton, the state changes like in the second pic.
Only the selection of the last row checkboxes are in the selection list.
So I m thinking about, if it is a problem with the selection list and its managed bean.
I tried a changelistener on the checkbox but I cant get the status of the checkbox, checked or unchecked.
So I removed it again.
<p:datatable>...
<p:rowExpansion>#{pathSearch.selectedSignalList1}
<p:selectManyCheckbox id="signalSel" value="#{pathSearch.selectedSignalList1}">
<p:ajax listener="#{pathSearch.changeSignalListener1}" />
<f:selectItems value="#{pathDistSel.availableSignalList}"
var="sig" itemValue="#{sig.label}___#{sig.idS}" itemLabel="#{sig.label}" />
</p:selectManyCheckbox>
</p:rowExpansion>
Bean snippet
#ManagedBean
#SessionScoped
public class PathSearch implements Serializable {
private List<Signal> selectedSignalList1;
getter, setter...
}
I would be happy, if someone has an idea, where my mistake in thinking is. Perhaps you have an example of something like this.
By using
<p:selectManyCheckbox id="signalSel" value="#{pathSearch.selectedSignalList1}">
<p:ajax listener="#{pathSearch.changeSignalListener1}" />
<f:selectItems value="#{pathDistSel.availableSignalList}"
var="sig" itemValue="#{sig.label}___#{sig.idS}" itemLabel="#{sig.label}" />
</p:selectManyCheckbox>
In the row expansion, the selection list in each row points to the same property, selectedSignalList1. You need to use a list/array there with the row index as a key, or a hashmap and a business key. So something like
<p:datatable ... rowIndex="signalIndex">
<p:selectManyCheckbox id="signalSel" value="#{pathSearch.selectedSignalList[signalIndex]}">
<p:ajax listener="#{pathSearch.changeSignalListener}" />
<f:selectItems value="#{pathDistSel.availableSignalList}"
var="sig" itemValue="#{sig.label}___#{sig.idS}" itemLabel="#{sig.label}" />
</p:selectManyCheckbox>
</datatable>
And in the bean
#ManagedBean
#SessionScoped
public class PathSearch implements Serializable {
private ArrayList<List<Signal>> selectedSignalList = new ArrayList<>();
getter, setter...
}
This way, each p:selectManyCheckbox in the p:rowexpansion has its own list backing the local selection
What you do on submission and how to aggregate or restore when you need to load the submitted data e.g. from a database and show it on screen is up to you to implement.

p:datascroller only lazyload one time

We have implement the datascroller from primefaces and extend it with lazy=true this works fine for the first lazy loading. But when I scroll down no event is fired from Frontend. The Backend works as expected.
I test it with changing the chunk size and get a better understanding what is happen in the backend code. We override the load function what primefaces is calling when you reach the end of page. I changed the code to not lazyloading by scrolling, therefor i implement a button to load the next chunk when you click the button. But Button disappear after clicking it once.
Xhtml:
<p:dataScroller value="#{stakeholderOverviewController.model}" var="stakeholder" chunkSize="50" lazy="true" rowIndexVar="test">
<f:facet name="header">
Scroll Down to Load More Cars
</f:facet>
<f:facet name="loader">
<p:commandButton type="button" value="More" icon="pi pi-chevron-circle-down"/>
</f:facet>
<h:panelGrid columns="2" style="width:100%" columnClasses="logo,detail">
<p:outputPanel>
<h:panelGrid columns="2" cellpadding="5">
<h:outputText value="Id:" />
<h:outputText value="#{stakeholder.lastname}" style="font-weight: bold"/>
<h:outputText value="Year:" />
<h:outputText value="#{stakeholder.firstname}" style="font-weight: bold"/>
</h:panelGrid>
</p:outputPanel>
</h:panelGrid>
<ui:include rendered="#{empty stakeholder}" src="/WEB-INF/compositions/stakeholderEmptyModel.xhtml" />
</p:dataScroller>
there is the backend code for Controller:
public StakeholderOverviewController() {
model = new LazyDataModel<StakeholderSearchWrapper>() {
#Override
public List<StakeholderSearchWrapper> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map filters) {
List<StakeholderSearchWrapper> execQuery = execQuery(first, pageSize);
return execQuery;
}
};
model.setRowCount(0);
}
....
protected synchronized List<StakeholderSearchWrapper> execQuery(int first, int pageSize) {
if (tmpfirst != first || tmppageSize != pageSize || reExecQuery) {
reExecQuery = false;
tmpfirst = first;
tmppageSize = pageSize;
tmpModel = new LinkedList<>();
// Query for stakeholder
queryresponse = stakeholderService.findByLastnameOrFirstmanOwnerOrShare(getSearchText(), getClientId(),
first, pageSize, selectedFacets, nameSort[getSort()]);
setFacets(queryresponse.getFacetFields());
Set<String> stakeholderIds = new HashSet<String>();
// convert for view
for (SolrDocument details : queryresponse.getResults()) {
StakeholderSearchWrapper stakeholderSearchWrapper = new StakeholderSearchWrapper(details);
tmpModel.add(stakeholderSearchWrapper);
stakeholderIds.add(stakeholderSearchWrapper.getId());
}
historyCount = stakeholderService.countHistoryEntryByStakeholders(stakeholderIds);
notesCount = stakeholderService.countNoteEntriesByStakeholders(stakeholderIds);
assignedProjects = projectService.findByStakeholderIds(stakeholderIds);
for(StakeholderSearchWrapper dao : tmpModel) {
dao.setHistoryCount(getHistoryCount(dao.getId()));
dao.setNoteCount(getNoteEntriesCount(dao.getId()));
dao.setProjects(getAssignedProjects(dao.getId()));
}
SolrDocumentList result = queryresponse.getResults();
int numFound = (int) result.getNumFound();
return tmpModel;
} else {
return tmpModel;
}
}
What i want is that the lazy loading dont fire only once. It should fire as often as needed. I dont know where i have to set some variable to do this. When im debuging the current code the load function is only requested at the first run of the page refresh and than one lazy loading is called but then nothing happens.
I think I realized the trick. Check the line:
model.setRowCount(0);
Instead, set the model row count properly using the max amount of data you want to display:
int totalNumberOfRegisters = this.count(/* relevant paramenters*/);
model.setRowCount(totalNumberOfRegisters);

getting inputText on only the first row of a (Primefaces) dataTable, with dynamic columns and rows

I have a Primefaces (5.0) Datatable with dynamic columns as well as dynamic rows.
In brief, my problem is how do I set it up so that the cells of the first row are a series of inputText's or inputTextarea's, while the rows below the first are outputText?
To elaborate, the xhtml (following BalusC's response to Creating and populating a DataTable dynamically in JSF2.0) is as follows:
<p:dataTable var="rowMap" value="#{reviewController.rowMapList}" scrollable="true" scrollWidth="900px" >
<p:columns value="#{reviewController.columnNameList}" var="columnName" headerText="#{columnName}">
<f:facet name="header" >#{columnName}</f:facet>
<f:facet name="header"><p:inputTextarea value="#{rowMap[columnName]}" /></f:facet>
<h:outputText value="#{rowMap[columnName]}" />
</p:columns>
</p:dataTable>
So I'm thinking, likely there is a problem in using facets for the row with the inputTexts, because the facet doesn't seem to take part in the dataTable data iteration.
So how do I put the first row in as a "normal" row (rather than a facet if that is part of the problem) -- but still apply inputTextarea's to that first row, without applying inputTextareas to all the rows?
For the purposes of getting it working, I've set it up statically with a #PostConstruct on reviewController, as follows.
#Named
#ViewScoped
public class ReviewController implements Serializable {
private List<String> columnNameList;
private ArrayList<Map<String, Object>> rowMapList;
private Map<String, Object> rowMap;
#PostConstruct
public void init() {
columnNameList = new ArrayList<>();
rowMapList = new ArrayList<>();
columnNameList.add("Source Report");
columnNameList.add("Overview");
columnNameList.add("Question 1");
columnNameList.add("Question 2");
columnNameList.add("Question 3");
Map<String, Object> m = new HashMap<>();
m.put(columnNameList.get(0), "Assessor1");
m.put(columnNameList.get(1), "Assessor1 overview text");
m.put(columnNameList.get(2), "Assessor1 comment on Q1");
m.put(columnNameList.get(3), "Assessor1 comment on Q2");
m.put(columnNameList.get(4), "Assessor1 comment on Q3");
rowMapList.add(m);
m = new HashMap<>();
m.put(columnNameList.get(0), "Assessor2");
m.put(columnNameList.get(1), "Assessor2 overview text");
m.put(columnNameList.get(2), "Assessor2 comment on Q1");
m.put(columnNameList.get(3), "Assessor2 comment on Q2");
m.put(columnNameList.get(4), "Assessor2 comment on Q3");
rowMapList.add(m);
//etc
}
Visually it turns out like this:
What I want is to have the first row being a row of InputTextarea's, so the user can add comments in each first row cell in response to the content of the rows beneath it. I've achieved it visually - but as I remark above, it doesn't work in that the inputTextareas (I presume because they're in facets) are not hooked into the rowMap and columnName iterations
I have looked at the primefaces editable datatable (http://www.primefaces.org/showcase/ui/data/datatable/edit.xhtml) - which is somewhat along the lines of what is required. However it makes all rows editable, not just the first. There is the further complexity in this case that both columns and rows will be dynamically applied.
Any comment or assistance appreciated. Thanks.
Since you asked you want only the 1st row is editable, I have asked you to use rowIndexVar to manage the rows.
You can use p:dataTable's rowIndexVar attribute.
rowIndexVar is Name of iterator to refer each row index.
Whose iterator name can be used in EL to get the Row Id
For example: if rowIndexVar="row" then you can access each row Index using that iterator name in EL using #{row}.
Example Code:
<p:dataTable rowIndexVar="row" value="..." var="myVar">
<p:column>
<p:inputTextarea rows="2" cols="25" counter="display"
value="#{myVar.myText}" rendered="#{row==0}" id="comment1"
maxlength="200"
counterTemplate="{0} remaining characters"
autoResize="false">
</p:inputTextarea>
<h:outputText value="#{myVar.myText}" rendered="#{row!=0}" />
</p:column>
....
</p:dataTable>
Thank you again to Unknown: This is effectively your answer (I don't know if there is a more formal way of attributing it to you, but I am happy to do so ). Much appreciated. xhtml code as follows:
<p:dataTable var="rowMap" rowIndexVar="row" value="#{reviewController.rowMapList}" >
<p:columns value="#{reviewController.columnNameList[0]}" var="columnName" headerText="#{columnName}">
#{rowMap[columnName]}
</p:columns>
<p:columns value="#{reviewController.columnNameList}" columnIndexVar="col" rendered="#{col!=0}" var="columnName" headerText="#{columnName}" >
<f:facet name="header" >#{columnName}</f:facet>
<h:inputTextarea value="#{rowMap[columnName]}" rendered="#{row==0}" />
<h:outputText value="#{rowMap[columnName]}" rendered="#{row!=0}" />
</p:columns>
</p:dataTable>
So it is as you described it for the rows, plus I have added an extra p:columns block just to differentiate the first column, and placed a columnIndexVar with rendered="#{col!=0}" conditional on the main p:Columns block so that the first column does not have the inputTextarea that the other cells of the first row do.

primefaces dataTable: how to copy paste row

I use primefaces,
my table has more than 75 column,
how to do when I want to copy row and paste it in the same table (of course change the PK after)
you can use the <p:hotkey> (to capture your copy and paste) along with the selectionMode attribute of <p:dataTable>
you can try:
<h:form>
<p:hotkey bind="ctrl+c" actionListener="#{Bean.copy}"/>
<p:hotkey bind="ctrl+s" actionListener="#{Bean.paste}"/>
<p:dataTable id="dceTable" value="#{Bean.list}"
var="row" selection="#{Bean.selection}"
rowKey="#{row.seqNo}" >
<p:column selectionMode="multiple" style="width:30px;text-align:center" />
... insert your columns here
</dataTable>
</h:form>
and your backing bean:
#Named("DCEBean")
#ViewScoped
public class Bean implements Serializable {
public void copy(){
// to copying codes like change PK
}
public void paste(){
// insert in your copied rows
}
}
You can't out of the box. Only thing I can think of is to create a context menu and/or intercept the ctrl-v/c keys and call some backing bean methods that all does this for you. But that would be to complex of a solution to create for you.
thank for all answers
but i have a little solution:
in the "list.xhtml" file and at context menu panel add the line:
<p:menuitem value="Copy" onclick="document.getElementById('CustomerListForm:copyButton').click();" icon="ui-icon-copy"/>
at the end of the file with other buttom add the line:
<p:commandButton id="copyButton" style="visibility: hidden; icon="ui-icon-copy" value="Copy" update=":CustomerCreateForm" oncomplete="PF('CustomerCreateDialog').show()"/>
for more click here

Primefaces Datatable dynamic calculation

In my view layer, I use a Primefaces Datatable with 9 columns, which the last one has float values (column name= 'Valor').
There is an integer column named 'prefDep' which has a filter for its values (foreignKey values, by the way):
<!-- column name omitted -->
<p:column sortBy="#{item.prefDep}" filterBy="#{item.prefDep}">
<h:outputText value="#{item.prefDep}"/>
</p:column>
The last row of the Datatable has a fixed GrandTotal, which is the overall sum of the mentioned 'Valor' column:
<p:columnGroup type="footer">
<p:row>
<p:column colspan="8" footerText="Grand Total:" />
<p:column footerText="#{upbController.totalLosses}" />
</p:row>
</p:columnGroup>
Method in the controller class to get the overall sum:
public int getTotalLosses() {
int total = 0;
for(Upb id : getItems()) {
total += id.getValor();
}
return total;
}
What I want: as I filter the Datatable, I want to show the corresponding GrandTotal for that filtered portion of values only, and not the 'fixed' overall sum.
How can I do it?
Thanks in advance.
--
After section:
In my AbstractController I have:
private List<T> filteredUpb;
//...
public List<T> getFilteredUpb() {
if (filteredUpb == null) {
filteredUpb = this.ejbFacade.findAll();
}
return filteredUpb;
}
In my UpbController (managedBean):
private List<Upb> filteredUpb = null;
//...
public int getPerdasTotal() {
int total = 0;
for (Upb id : getFilteredUpb()) {
total += id.getVlOco();
}
return total;
}
In jsf:
<p:column>
<f:facet name="footer">
<h:outputText value="#{upbController.perdasTotal}" />
</f:facet>
</p:column>
Just to not let this question unanswered, the solution (see below) for this question can be found in this other post of mine: Dynamic Calculation Using Filter in Datatable
I think that you can define AJAX calls on PF datatable filtering and/or paging.
<p:dataTable value="#{myBackingBean.values}" var="row" filteredValue="#{myBackingBean.filteredValues}" >
<p:ajax event="filter" listener="#{myBackingBean.updateSum}" update="componentToUpdate" />
//...
</p:dataTable>
In your updateSum() method, you can walk through the filteredValues collection to do your business.
You may have some difficulties to target the update component enclosed in the datatable but this can be resolved by adding something like styleClass="footerToUpdate" to your code and using it in the update with update="#(.footerToUpdate)"