Primefaces lazy Datatable with no total rows count - jsf-2

I have a PrimeFaces DataTable where I have pagination (the component one, no custom) in form of two arrows (back and forward).
The table never knows how many rows are there, so I have the following function to set the rows count (keep in mind that the dataSize parameter is for a pageSize of 25 rows always 25 or less):
if(pageNumber == 0 && dataSize < pageSize) {
setRowCount(dataSize);
} else if(pageNumber != 0 && dataSize < pageSize) {
setRowCount(pageNumber*pageSize + dataSize);
} else {
setRowCount(pageSize*(pageNumber+1) + 1);
}
With this function called in the load method after fetching the data, there are always one page or two pages or three pages. Depending on the current position of the first record the back arrow or the forward arrow are disabled or enabled. This all works well so far.
Now I want to be able to load the table on a given page. If I have a pageNumber the correct set of data is displayed but only the forward arrow is enabled. So the DataTable thinks that it is on the first page. I tried to call the setRowIndex() before or after my function but no luck.
Does anybody know how to make DataTable think that there is also a page before the current one?

Related

pdfmake problem displaying accurate page numbers

I'm using pdfmake to generate a document that contains a number of sub-documents, e.g. a PDF containing invoices; each invoice is a logical document inside the PDF document.
I have a requirement that in the footer or header of each page I show "Page of " where both those numbers are relative to a single invoice, and not the overall document.
The header and footer callbacks look good, but only specify the page number and count relative to the entire document, and the pageBreakBefore callback doesn't generate anything like the documented information, and in any case I can't figure out how I could use it here.
This doesn't seem like a unique requirement, so hopefully I'm just missing something obvious.
Thanks!
I believe that pdkmake's header and footer function's arguments can only contain the global pageCount.
There is however a way to reproduce the behavior that you want, if you handle manually the pageBreaks :
const realPageIndexes = [];
let currSubPdfIdx = 0;
let currPageCountForSubPdf = 0;
const getPageBreak = (newSubPdfIdx: number) => {
if (currSubPdfIdx !== newSubPdfIdx) {
currSubPdfIdx = newSubPdfIdx;
currPageCountForSubPdf = 0;
} else {
currPageCountForSubPdf += 1;
}
realPageIndexes.push(currPageCountForSubPdf);
return {text: '', pageBreak: 'after'}; // return elem causing the pageBreak
}
The footer function, filling the page numbers, is called once the document definition generation is done.
If you handle every overflow by yourself, by calling getPageBreak(currentSubPdfIndex) at the end of each page, you will end up knowing which sub-Pdf is displayed in each page :
I display part of the 1rst subPdf in page 1:
I display the end of 1rst subPdf in page 2
I display 2nd subPdf in page3
I display 3rd subPdf in page4 ....
.
let subPdfIdx = 0;
pdfContent.push(subPdf1FirstPart)
pdfContent.push(getPageBreak(subPdfIdx))
pdfContent.push(subPdf1SecondPart)
pdfContent.push(getPageBreak(subPdfIdx))
subPdfIdx++;
pdfContent.push(subPdf2)
pdfContent.push(getPageBreak(subPdfIdx))
subPdfIdx++;
pdfContent.push(subPdf3)
pdfContent.push(getPageBreak(subPdfIdx))
realPageIndexes[] then looks like :
[ 1, 1, 2, 3 ];
The only step left is to tell the footer function to use the page counts we just created instead of the global page count :
const documentDefinition = {
content: [YOUR CONTENT],
footer: (currPage, allPages) => 'subPdf index is ' + realPageIndexes[currPage];
}
Please note that this method will fail if you don't handle overflows correctly:
if for example a paragraph is too big to fit a page, and you call getPageBreak() after that, pdfmake will automatically create a new page for the end of the paragraph (page which is untracked in our realPageIndexes) and then add the page caused by your getPageBreak() right after the end of the text. So just make sure not to overflow the page :)

Adding a scrollbar to ui-grid overlays the bottom row

An application that I'm working on has a ui-grid on the page. Recently, we added a horizontal scrollbar to the grid and also enabled "pinning" so that some of the columns could be frozen while the grid was scrolled horizontally.
One odd behavior that I've noticed is that the newly added scrollbar overlays the last row in the table. Has anyone else ever experienced this and, if so, figured out how to stop it from happening? I've tried everything I could think of (i.e. changing row height, dynamic sizing of the grid, etc) but nothing is working. I'm hoping someone else has overcome this issue and can tell me how they did it.
Well, I found a way to resolve this, but it's particular to how our grids are created.
All of our grids that have horizontal scrolling have the first three columns pinned. I used the code below to see if the grid in question has the grid option for pinning. If the grid is scrollable, I simply add 15 to the grid's height. This makes the grid bigger so that there's plenty of room for the scrollbar without it overlaying the bottom row.
var scrollbarOffset = 15;
var buffer = 5;
var gridHeight = ((rowCount > 0 ? rowCount : 1) * rowHeight) + footer + header + filter + buffer;
var scrollable= $.grep(element[0].attributes,
function (rn) {
return rn.nodeName === "ui-grid-pinning";
}).length > 0;
if (scrollable) {
gridHeight = gridHeight + scrollbarOffset;
}

ui-grid - How to pin a ROW to the top

I'm using the angularjs ui-grid and I have a "total" row that I want to pin to the top of the grid no matter what is the current sorting.
How can I accomplish that?
I think this is what you are looking for : Row Pinning
Essentially add another hidden column, something like this:
{
field: 'pinned',
visible: false,
sort: {direction: uiGridConstants.ASC, priority: 0}, //use uiGridConstants.DESC for pinning to the bottom
suppressRemoveSort: true,
sortDirectionCycle: [uiGridConstants.ASC] //use uiGridConstants.DESC for pinning to the bottom
}
Row entities which have pinned = true rise to the top, even when other sorting are applied.
DISCLAIMER: I know it's not exactly answers the question, but this is how I solved it for now until I'll have a better solution:
Create an other grid above the main grid :
<div style="height:30px" ui-grid="totalGridOptions"></div>
<div ui-grid="gridOptions" class="grid"></div>
with definitions:
$scope.totalGridOptions = {showHeader:false,enableHorizontalScrollbar:false};
and then bind the columns of the main grid to the total grid (for width and other adjustments):
$scope.$watch('gridOptions', function (newVal) {
$scope.totalGridOptions.columnDefs = newVal.columnDefs;
}, true);
I think you should use something like this
$scope.gridOptions.data.unshift({label:value});
unshift adds it to the top
Edit 2 / Actual Solution: The way I finally settled this issue was by using custom header cell templates. I essentially create a second header row by adding a div at the bottom of what was previously my header. Here's a simple version:
<div class="super-special-header>
<div class="header-display-name">{{col.displayName}}</div>
<div class="totals-row">{{grid.appScope.totals[col.field]}}</div>
</div>
I store my totals on the controller's $scope and can access them in that div with grid.appScope.totals[whateverThisColumnIs]. This way I can still update them dynamically, but they don't get mixed into a sort function like my previous 'solution' was aiming for.
Edit 1 / Dead-end 'solution': Just ran into a problem with my solution, if your table is long (you have to scroll to get to bottom rows), the totals row will scroll out of view. Going to leave this 'solution' here so no one else makes the same mistake!
I had this same issue but with a twist. Since I was going to need to change the default sorting algorithms for many of the columns anyway, I set my algorithm up to skip the first element in the sort. You can use the sortingAlgorithm property on any columndef that would be part of a sortable column. This is really only a solution if you have only a few sortable columns though. It becomes unmaintainable for huge tables.
I couldn't find any built-in feature for ui-grid to keep a specific row at the top of the grid when sorting is applied. But this could be done using sortingAlgorithm parameter in the columnDefs( please refer to http://ui-grid.info/docs/#!/tutorial/Tutorial:%20102%20Sorting).
I have written an algorithm which keeps the row('total' is the particular cell value in the row) at the top of the grid without applying a sorting.
var sortingAlgorithm = function (a, b, rowA, rowB, direction) {
if (direction == 'total') {
if (a == 'total') {
return 0;
}
return (a < b) ? -1 : 1;
} else {
if (a == 'total') {
return 0;
}
if (b == 'total') {
return 1;
}
}
}

ios UITableView flash when delete and insert rows

I have a problem with UITableView.
I have a ringbuffer with maximum 20 element.
I add this buffer as source to a tableview.
When i add a item i call
table.BeginUpdate();
tableMessges.InsertRows(new NSIndexPath[] { NSIndexPath.FromRowSection(buffer.Count-1, 0) }, UITableViewRowAnimation.None);
buffer.Add(item);
tableMessges.EndUpdates();
This work fine.
Now when the buffer is full, i delete the first table row (also the first object in buffer), insert a new row and add a new object to buffer.
When i do this procedure slow (every second) it look fine.
I just want to have maximum 20 items in the table.
BUT when i call this procedure lets say ever 100 ms the the table view flashes a lot.
Is there any chance to reduce the flashing.
here is my code when the buffer is full.
table.BeginUpdates();
buffer.AddMessage(canMessage);
if(buffer.count >= buffer.capacity)
{
table.InsertRows(new NSIndexPath[] { NSIndexPath.FromRowSection(buffer.count-1, 0) }, UITableViewRowAnimation.None);
table.DeleteRows(new NSIndexPath[] { NSIndexPath.FromRowSection(0, 0) }, UITableViewRowAnimation.None);
}
else
{
table.InsertRows(new NSIndexPath[] { NSIndexPath.FromRowSection(buffer.NumberOfRows-1, 0) }, UITableViewRowAnimation.None);
}
table.EndUpdates();
I would suggest to instead inserting and deleting row directly to UITableView control, you should manipulate the DataSource binds to it and reload table data.
For optimize and specific data reload, you can look at the Apple Doc (Reloading the TableView) for UITableView control. For instance:
reloadRowsAtIndexPaths:withRowAnimation:
From your in-hand code, it looks you have certain details to go with this method.

How to avoid JQuery UI accordion with a long table inside from scrolling to the beginning when new rows are appended?

I have a table of many rows in a JQuery UI accordion.
I dynamically append the table this way:
var resJson = JSON.parse(connector.process(JSON.stringify(reqJson)));
for ( var i in resJson.entryArrayM) {
// test if entry has already been displayed
if ($("#resultTr_" + resJson.entryArrayM[i].id) == null)
continue;
$("#resultTable > tbody:last").append(listEntry.buildEntryRow(resJson.entryArrayM[i]));
}
Firstly I check if a row of the same tr id already exists. If not, I would append to the last row of the table.
It works. But the problem is: every time a row is appended, the accordion would scroll to the first row of the table. Since the table is remarkably long, it makes users inconvenient to scroll down again and again to watch newly-added rows. So how to avoid this?
First of all, just do one append rather than appending every time through the loop:
var resJson = JSON.parse(connector.process(JSON.stringify(reqJson)));
var seen = { };
var rows = [ ];
var trId = null;
for(var i in resJson.entryArrayM) {
// test if entry has already been displayed
var trId = 'resultTr_' + resJson.entryArrayM[i].id;
if($('#' + trId).length != 0
|| seen[trId])
continue;
rows.push(listEntry.buildEntryRow(resJson.entryArrayM[i]));
seen[trId] = true;
}
$("#resultTable > tbody:last").append(rows.join(''));
Also note that I corrected your existence test, $(x) returns an empty object when x doesn't match anything, not null. Not only is this a lot more efficient but you'll only have one scroll position change to deal with.
Solving your scrolling issue is fairly simple: find out what element is scrolling, store its scrollTop before your append, and reset its scrollTop after the append:
var $el = $('#whatever-is-scrolling');
var scrollTop = $el[0].scrollTop;
$("#resultTable > tbody:last").append(rows.join('')); // As above.
$el[0].scrollTop = scrollTop;
There might be a slight visible flicker but hopefully that will be lost in the noise of altering the table.
You could also try setting the table-layout CSS property of the <table> to fixed. That will keep the table from trying to resize its width or the width of its columns and that might stop the scrolling behavior that you're seeing. The downside is that you'll have to handle the column sizing yourself. But, you could try setting table-layout:fixed immediately before your append operation to minimize the hassle.

Resources