I have an enhancedGrid with 9000+ elements, initialized as follows:
var grid_invoices = new dojox.grid.EnhancedGrid({
id: 'grid_invoices',
keepSelection: true,
rowSelector: false,
selectable: true,
escapeHTMLInData: false,
rowsPerPage: 250,
keepRows: 1000,
delayScroll: true,
noDataMessage: "Aucun résultat",
queryOptions: {ignoreCase: true},
plugins: {
indirectSelection: {headerSelector:true, styles:"text-align: center;padding:5px;"},
search: false
}
},
document.createElement('div'));
I need to do a csv export of selected elements of the grid. If I select <= 1000 elements no problem, here the actual code
var downloadPdfIframeName = "downloadPdfIframe",
iframe = dojo.io.iframe.create(downloadPdfIframeName),
count = grid_invoices.selection.getSelectedCount();
if (count > 0 && count < 1000) {
var lst = getGridSelection('grid_invoices', 'id'),
ids = lst.join(',');
dojo.io.iframe.setSrc(iframe, "/invoices/exportcsv/ids/" + ids, true);
} else if (count == 0) {
dojo.io.iframe.setSrc(iframe, "/invoices/exportcsv?" + dojo.objectToQuery(getSearchFilterInvoice()), true);
}
now, I'd like to be able to export any elements I want (>1000 possibly) but I don't know where to start.
The grid is a lazy loaded grid so there's no possibility of retrieving all ids with one simple query, and even if that would be possible, I cannot just chain the ids to the GET HTTP QUERY (query limit)
I thought of doing something like "see if everything is selected and build a custom php filter" but if the user select, let's say, 5000 records, I don't have a clue of what to do.
Can someone point me to the good direction ?
Related
I inherited some code and I've been asked to add a new 'feature'. The existing code sets the expanded state to 'true' for all top-level nodes not named "Favorites", but the "fancytree-1-expanded" Value in LocalStorage is not set until the user explicitly collapses and expands another top-level node. I understand that this is the expected behavior, but I need to bypass this and set the "fancytree-1-expanded" LocalStorage Value is none exists (I know the values of the top-level nodes, so I can name them easily).
Here is my FancyTree init code with persist (and several remarks I've used to try to identify the data and get this going):
// start of UI integration
buildMenu = function() {
$("#" + Menu).fancytree({
extensions: ["filter", "glyph", "persist"],
quicksearch: true,
//debugLevel: 4,
source: getandformatdata,
filter: {
autoApply: true, // Re-apply last filter if lazy data is loaded
autoExpand: true, // Expand all branches that contain matches while filtered
counter: true, // Show a badge with number of matching child nodes near parent icons
fuzzy: false, // Match single characters in order, e.g. 'fb' will match 'FooBar'
hideExpandedCounter: true, // Hide counter badge if parent is expanded
hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
highlight: true, // Highlight matches by wrapping inside <mark> tags
leavesOnly: true, // Match end nodes only
nodata: true, // Display a 'no data' status node if result is empty
mode: "hide" // "dimm" = Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
},
glyph: {
preset: "awesome4",
map: {
expanderClosed: "expandIconSidebar",
expanderLazy: "expandIconSidebar",
expanderOpen: "expandedIconSidebar",
}
},
click: function (event, data) {
//needed to reapply classes
/*if($(data.originalEvent.target).hasClass('fancytree-expander')) {
//cannot apply class if not expanded
data.node.setExpanded(true);
//Add leaf node classes
$('.fancytree-node:not(.fancytree-folder)').closest('li').addClass('leafNode');
$('.fancytree-folder').closest('li').addClass("folderNode");
data.node.setExpanded(false);
}*/
if(data.node.isFolder()) {
//if(data.node.title==='Favorites') {
// $('#actGoHome').click();
//}
//else
return;
}
},
persist: {
store: "auto" // 'cookie', 'local': use localStore, 'session': sessionStore
},
postProcess: function (event, data) {
//if persist is not set then expand all folders except Favorite
//console.log("postdata", data);
//console.log("persist",$.ui.fancytree.getTree('#' + Menu).getPersistData());
let persistExpanded = $.ui.fancytree.getTree('#' + Menu).getPersistData().expanded[0];
//console.log("Expanded: "+persistExpanded);
let foldersExpanded = data.response.filter(d => { return persistExpanded.split("~").some(f => {return d.title === f})});
//console.log("foldersExpanded", foldersExpanded);
if (foldersExpanded.length == 0) {
data.response.forEach((d) => {
if (d.title != 'Favorites') {
d.expanded = true;
}
})
}
},
});
}
I've tried to look at using localStorage.getItem/setItem, but my site is in an iframe in another port from the parent app/site. This appears to be problematic for trying to directly get and set the LocalStorage, so I'm hoping to edit my FancyTree's ExtPersist code to set the LocalStorage value if none exists.
The following JSFiddle can be used if someone knows of a way to default in the Value for "fancytree-1-expanded" of "b9052f9a-1c45-47ef-9d66-8c2bded1230e~tr" if no Value exists - https://jsfiddle.net/vt8mrLt0/1/
I am using YUI Paginator API for pagination and I need to show Total number of pages on screen. I saw that there is a function getTotalPages() in API but I am unsure about how to use it, there isn't enough documentation. Also after looking at some other documentation I tried using {totalPages} but didn't work.
Can somebody help me out in this issue? Thanks in advance!!
Below is the code snippet I am using. Please refer to template object from config:
config = {
rowsPerPage: 100,
template :
'<p class="klass">' +
'<label>Total pages: {totalPages}</label>'+
'<label>Page size: {RowsPerPageDropdown}</label>'+
'</p>',
rowsPerPageDropdownClass : "yui-pg-rpp-options",
rowsPerPageOptions : [
{ value : 100 , text : "100" },
{ value : 250 , text : "250" },
{ value : 500 , text : "500" },
{ value : 1000 , text : "1000" },
{ value : tstMap[tabName].length , text : "All" }
],
};
var myPaginator = new YAHOO.widget.Paginator(config);
The Paginator utility allows you to display an item or a group of items depending on the number of items you wish to display at one time.
Paginator's primary functionality is contained in paginator-core and is mixed into paginator to allow paginator to have extra functionality added to it while leaving the core functionality untouched. This allows paginator-core to remain available for use later on or used in isolation if it is the only piece you need.
Due to the vast number of interfaces a paginator could possibly consist of, Paginator does not contain any ready to use UIs. However, Paginator is ready to be used in any Based-based, module such as a Widget, by extending your desired class and mixing in Paginator. This is displayed in the following example:
YUI().use('paginator-url', 'widget', function (Y){
var MyPaginator = Y.Base.create('my-paginator', Y.Widget, [Y.Paginator], {
renderUI: function () {
var numbers = '',
i, numberOfPages = this.get('totalPages');
for (i = 1; i <= numberOfPages; i++) {
// use paginator-url's formatUrl method
numbers += '' + i + '';
}
this.get('boundingBox').append(numbers);
},
bindUI: function () {
this.get('boundingBox').delegate('click', function (e) {
// let's not go to the page, just update internally
e.preventDefault();
this.set('page', parseInt(e.currentTarget.getContent(), 10));
}, 'a', this);
this.after('pageChange', function (e) {
// mark the link selected when it's the page being displayed
var bb = this.get('boundingBox'),
activeClass = 'selected';
bb.all('a').removeClass(activeClass).item(e.newVal).addClass(activeClass);
});
}
});
var myPg = new MyPaginator({
totalItems: 100,
pageUrl: '?pg={page}'
});
myPg.render();
});
I'm using jquery jTable in my web-based project. Its pagination work fine when the number of rows less than 10,000. But when the number of rows exceeded from 10000, it causes an unfamiliar issue in its pagination. The pagination start skipping odd page numbering. You can see it in picture below:
My javaScript code for jTable is:
$("#AllProductTable").jtable({
paging: true,
pageSize: 10,
columnSelectable: false,
actions: {
listAction: '/ProductDefinition/Select'
},
fields: {
ProductId: { visibility: 'hidden', listClass: 'right-align' },
ProductCode: { title: 'Product Code' },
// more fields...
},
recordsLoaded: function (event, data) {
}
});
while my html tag for jTable is:
<div id="AllProductTable" style="width:400%;"></div>
I explored a lot\, but didn't find the solution related to it. I'm further unable to understand this miss behavior.
And at last, I got succeeded to solve this issue. I go through the jquery.jtable.js whole file and find something strange in one of its function. The function was;
_refreshGotoPageInput: function() {
// different logic statements
//...
//Skip some pages is there are too many pages
var pageStep = 1;
if (currentPageCount > 10000) { // if you having more than 10,000 pages
pageStep = 100;
} else if (currentPageCount > 5000) { // if you having more than 5000 pages
pageStep = 10;
} else if (currentPageCount > 2000) { // if you having more than 2000 pages
pageStep = 5;
} else if (currentPageCount > 1000) { // if you having more than 1000 pages
pageStep = 2;
}
//....
// Differnet logic statements
}
All you need is just to comment this portion of the above given function, or modify it according to your own implementation logic.
In my above mentioned case, my no_of_pages were increasing from 1000, that's why its taking 2-steps on changing page and so on.
I have a problem with data validation on an editable cell. I make some cells of one column editable based on the values of these cells. Here is the code of the grid:
jQuery("#cart").jqGrid({
datatype: 'local',
autowidth: true,
height: tabHeight,
gridview: true,
shrinkToFit: true,
autoencode: true,
loadtext: "იტვირთება",
multiselect: true,
idPrefix:"b",
colNames: ['დასახელება', 'რაოდენობა მინ.', 'რაოდენობა მაქს.', 'პოზიცია', 'რაოდენობა', 'ფასი'],
colModel: [{
name: 'asktx',
index: 'asktx',
width: 40,
sorttype: "string",
},
{
name: 'menge_min',
index: 'menge_min',
width: 30,
sorttype: "number",
hidden: true
},
{
name: 'menge_max',
index: 'menge_max',
width: 30,
sorttype: "number",
hidden: true
},
{
name: 'srvpos',
index: 'svrpos',
hidden: true
},
{
name: 'menge',
index: 'menge',
width: 30,
sorttype: "number",
editrules: {
required: true, number: true, custom: true, custom_func: checkInterval
},
editable: true,
},
{
name: 'price',
index: 'price',
width: 30,
sorttype: "string",
search: false,
}],
viewrecords: true,
caption: "არჩეული მომსახურებები",
gridComplete: function () {
var $this = $(this), ids = $this.jqGrid('getDataIDs'), i, l = ids.length;
for (i = 0; i < l; i++) {
var rowData = jQuery(this).getRowData(ids[i]);
if (rowData.menge_min != rowData.menge_max && !rowData.menge) {
menge_min = rowData.menge_min;
menge_max = rowData.menge_max;
$this.jqGrid('editRow', ids[i], true);
}
}
}
});
I am using gridComplete to check if values of two cells are equal and if they are not, I am making a column called "menge" editable in that row. I have also done the validation for constant values, like if I need to check whether this menge value is between a and b, I can do that and all is fine, although now I need to validate that field value based on the values of "menge_min" and "menge_max" field values, that are hidden. I see that custom function "checkInterval" can have only two parameters, so I can not pass the row ID there. Is there any way for getting some kind of information about the row which is currently being edited in custom validation function?
I fill the grid based on click event on another grid, here is the validation function for now:
var checkInterval = function (value, colname) {
value = parseInt(value);
mange_min = parseInt(menge_min);
menge_max = parseInt(menge_max);
if (value < menge_min || value > menge_max) {
return [false, "რაოდენობა უნდა იყოს " + menge_min + "-" + menge_max + " ინტერვალში"];
} else {
return [true];
}
}
As for multiple editable rows, it is kind of requirement and the user knows he/she entered a correct value if there is no validation error popup and continue to edit other rows. The jqGrid version is 4.5.1.
here is the select event of the other grid:
onSelectRow: function (id) {
if (id && id !== lastSel) {
jQuery(this).restoreRow(lastSel);
lastSel = id;
var celValue = jQuery(this).getRowData(id);
var rowCount = $("#cart").getGridParam("reccount") + 1;
if (celValue.menge_min == celValue.menge_max )
celValue.menge = celValue.menge_min;
var newID = id + rowCount;
jQuery("#cart").jqGrid('addRowData', newID, celValue);
}
}
I see that your problem is accessing to the content of menge_max in menge_min column inside of custom_func callback.
You current code set menge_min and menge_max to the values from the last row of the grid. The loop inside of gridComplete overwrite the value of the variables.
I would recommend you don't start editing mode for multiple rows. It generates many problems. For example the rows could be modified, but not saved. The sorting and filtering (searching) are blocked in the grid. One can use the possibility only after saving of changes in all rows. Typically one implement starting of editing on click on any cell of the grid. The user sees the starting of editing and he/she can press Enter to save the data. Between editing the rows the user can sort or filter the grid (you can add call of filterToolbar method to create the filter bar). In the way the user can easy find the requested row of data and then edit the data.
The version 4.5.1, which you currently use, is very old. It was published about 3 years ago. You have no callbacks with exception custom_func, which you can use and because you edit multiple rows at the same time.
The simplest way to solve your problems would be updating to free jqGrid 4.13.0. Free jqGrid is the fork of jqGrid which I develop starting with the end 2014. It have enchantment which you need. I implemented alternative way to specify custom validation. Instead of usage custom: true, custom_func: checkInterval one can use custom: checkInterval instead (custom property is not Boolean, but callback function instead). In the case the custom callback would get one parameter options with many properties which you can use. The properties are described here (see the comment for additional information). You could need options.rowid and to use var item = $(this).jqGrid("getLocalRow", options.rowid); to get full item of data where item.menge_max and item.menge_min can be used.
Additionally I would recommend you to use addRow inside of onSelectRow or parent grid. You can use initdata option to specify the data of the grid and no rowID. The documentation of addRow described the default value of rowID incorrectly. The default value of rowID is null, even for the version 4.5.1 (see the line of code), and jqGrid generates unique rowid automatically in the case. You can then remove the code from gridComplete. If you would implement starting of inline editing on selection the rows in the grid then the user can do all what he need without any restrictions.
The last remark. If you would migrate to free jqGrid you can remove menge_min, menge_max and svrpos columns from the grid. Instead of that you can use additionalProperties: ["menge_min", "menge_max", "svrpos"]. It informs free jqGrid to save the value of the properties in the local data, but not place the information in some hidden cells of the grid (no unneeded information in the DOM of the page).
In any way you can remove unneeded sorttype: "string" and index properties from colModel.
I am trying to implement Handsontable into our reporting system. Ive done everything i wanted except one thing. I am highlighting error cell using renderers (simply by setting red color on background). However when i remove row by context menu->remove row, all renderers remain on their x-y positions. I'd like them to follow their rows instead.
$container.handsontable({
data: data,
rowHeaders: true,
colHeaders: true,
minSpareRows: 1,
contextMenu: true,
stretchH: 'all',
comments: true,
cells: function(row, col, prop) {
var cellProperties = {};
{foreach $excelError as $error}
if (row === {$error['row']} && col === {$error['col']}) {
cellProperties.renderer = {$error['renderer']};
cellProperties.comment = {$error['desc']};
}
{/foreach}
if (row === 0) {
cellProperties.renderer = firstRowRenderer;
}
return cellProperties;
}
});
Despite variable name $error it also contents correct row data which just pain cells with white color. The errorRenderer function looks like this (white one is similar to this one)
var errorRenderer = function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
$(td).css({
background: 'red',
color: "white",
fontWeight: "bold"
});
};
Any idea how to fix that? I'd be fine even just with catch of that remove row event as i can call ajax and rerender cells again.
Thank you for any ideas.
EDIT:
Fixed solution:
var cellsProp = new Array();
{foreach $excelError as $error}
cellsProp[{$error['row']}] = new Array();
cellsProp[{$error['row']}][{$error['col']}] = {$error['renderer']|noescape};
{/foreach}
var removing = false;
$container.handsontable({
data: data,
rowHeaders: true,
colHeaders: true,
minSpareRows: 1,
contextMenu: true,
stretchH: 'all',
comments: true,
cells: function(row, col, prop) {
var cellProperties = {};
if (typeof cellsProp[row] != "undefined") {
cellProperties.renderer = cellsProp[row][col];
}
if (row === 0) {
cellProperties.renderer = firstRowRenderer;
}
return cellProperties;
},
beforeRemoveRow: function(index, amount) {
if (removing == false) {
for (x = index; x < cellsProp.length; x++) {
cellsProp[x] = cellsProp[x+1];
}
}
removing = true;
},
afterRemoveRow: function(index, amount) {
removing = false;
}
});
The error is in the PHP. When a row is deleted, your PHP still thinks that a specific row index is in error. If you could you provide a function that would remove a row from your PHP, we can create a function beforeRemoveRow which triggers after a row is removed.
In here we can call your PHP function and give it the row index to remove. Your program should now work.
The function would look like this, and would be added to the list of options on your hot definition:
beforeRemoveRow: function(index, amount) {
phpFunctionRemoveRow(index);
}
EDIT:
With this logic of hard-coded coordinates, removing a row would always give you the behavior of turning (x,y) red. This is what was meant by having an "error" in your php. It means that since you receive the coordinates with errors from PHP, you can't expect the hardcoded index values to change.
Something still doesn't make sense. If you have no interaction with PHP after render (you don't call it after render), and you say that PHP is correct, then does this mean you're never going to be modifying your table? What if someone makes a change and you have to re-validate your table?
If, however, you are capable of doing this, then what I would do is the following:
After a change to the table, call your php function to revalidate. This will change that errors array you have in php but because the cells option is static, you won't see changes. At that point, you'd use:
hotInstance.updateSettings({ cells: getCellsAgain() });
In that function, getCellsAgain(), you'd call your php code again to tell you the new indeces. That's the best you'll get without doing the validation on the JS side.