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.
Related
I'm new using kendo grid UI, i'm trying to make a non editable column (when updating) using a simple code :
schema: {
id: 'ID',
fields: {
id: { editable: false }
}
}
This default schema, makes by default non editable id column, and i can't even create a new row with id .
I want to make it non editable (when updating) but i want the possibility to create a row and assign an id from user (when creating).
Any ideas ?
Edit :
PS : the proprety is not related to only id, it can be on every column (can't update but can create)
The editable required a function instead of a value.
columns: [
{ field: 'value', editable: function () { return false; } }
],
Checkout here:
https://dojo.telerik.com/oROJayAd
I always doubt about that model editable option. It never really worked for me. It should have something very deep in the setup to make it work which I never realized what it. So this is a way to acomplish what you need that I know it indeed works: To cancel the edit event. Check it out:
edit: function(e) {
// Cancels a new row
if (arguments, e.model.isNew()) {
this.cancelRow(e.container.parent());
}
else { // Cancels a cell editing
this.closeCell(e.container);
}
}
Demo
Now, if you like to add a condition in that event based on what you have set in your model, you can access it within event as well:
edit: function(e) {
let currentColumn = this.options.columns[e.container.index()].field,
model = this.dataSource.options.schema.model.fields[currentColumn];
if (model.editable === false) {
// Cancels a new row
if (arguments, e.model.isNew()) {
this.cancelRow(e.container.parent());
}
else { // Cancels a cell editing
this.closeCell(e.container);
}
}
}
Demo
You can add an option yourself in the model to set if the column can be updated or only created, and handle that information inside the event, canceling the editing whenever you like.
This is how I just did it, though there are other ways.
In columns option if you remove the field option from a column it doesn't know from where to bind it.
Then use the template option to show(bind) the id. Thus making it readonly
columns: [
{
title: 'Id', width: "40px",
template: "#= id #",
},
...]
My table contains thre columns; "name", "description" and "status". I have a dropdown field which filters the table on the status column. Essentially:
$('.js-status-dropdown').dropdown({
onChange: function (value) {
$('#dt').DataTable().column('status:name').search(value).draw();
}
});
This works, but the problem is the standard free-text search input field includes the status field in the free-text search.
Setting searchable: false on the status field causes the dropdown to stop working since Datatable ignores it.
{
data: 'status',
name: 'status',
searchable: false // Stops table.column().search(value) from working :-(
}
Ideally, the (standard) free-text search field should ignore the stuatus column, but the dropdown code should still be working.
This works:
Set the column to searchable: false. This makes the table ignore this column in free text searches.
Add a custom search which uses the original row data, settings.aoData, instead of the data array (it doesn't contain the column because of 1.)
Redraw the table when the filter dropdown changes.
Code:
$('#dt').DataTable(defaults)
.on('init.dt', statusHandling);
function statusHandling(e, settings, processing) {
// Redraw table on dropdown status change
$('.js-status-dropdown').dropdown({
onChange: function (value) {
$(options.table).DataTable().draw();
}
});
// Our custom search function which adds an AND 'status = foo' condition
$.fn.dataTable.ext.search.push(
function (settings, data, dataIndex) {
var input = $('input[name=status]').val().toLowerCase();
// Use original data instead of 'data' because status is not searchable
var status = settings.aoData[dataIndex]['_aData']['status'];
return status.toLowerCase().indexOf(input) === 0;
}
);
}
I have a jqgrid that is completely populated locally. I have a select element in the grid aswell. This is produced as shown below
{ name: 'sparkline', width: 200, editable: true, edittype: 'select', formatter: 'select', editoptions: { value: sparklinedropdownstring }, classes: "extracellpadding" }
the variable 'sparklinedropdownstring' is a string set out like the examples from http://www.trirand.com/jqgridwiki/doku.php?id=wiki:common_rules
"value:label;value1:label1;value2:label2;value4:label5;"....etc etc
Now whenever I use the search bar or sort it always sorts or searches based on the value and not the label, I was wondering if i could search via the label instead? there are a few times where the value doesn't have a lot in common with the label and the average user would search for a label rather then a programmer's value.
My search and sort toolbars are setout like so:
myGrid.jqGrid('navGrid', '#mypager', { edit: false, add: false, del: false, search: false });
myGrid.jqGrid('filterToolbar', { stringResult: true, searchOnEnter: false, defaultSearch: "cn" });
If you use formatter: "select" with "value:label;value1:label1;value2:label2;value4:label5;" then only value, value1, value2 etc. are in your input data. So jqGrid sort and search by the values.
If one need to sort by another value then one can use sorttype as function. It allows to replace the values from the grid to another values which will be used during sorting instead of original values in the column.
To search in column which have formatter: "select" one uses typically stype: "select" and searchoptions: {sopt: ["eq", "ne"], value: ":All;" + sparklinedropdownstring } (the searchoptions.value will be used the same as editoptions.value or formatoptions.value). As the result the user sees <select> with the same texts in the filter toolbar. So the user just have no other possibility as to choose some option by text. The additional item ":All;" in searchoptions.value are needed to allow the user to remove the filter in the column and to display all unfiltered items ("All" is the text and empty string "" is the value of the corresponding option of select).
I've following issue: I have two kogrids on my page. One one the left, one on the right side.
Between them I added two buttons so that the user can move selected items from the left to the right side and the other way round.
Therefor my view model has 4 arrays, ItemsA, SelectedItemsA, ItemsB and selectedItemsB. The two kogrids are configured as followed:
<div data-bind="koGrid: {
data: ItemsA,
selectedItems: SelectedItemsA,
displaySelectionCheckbox: false,
enableRowReordering: true,
enableSorting: false,
columnDefs: [
{ field: 'Name', cellTemplate: tableCellTemplate, cellClass: 'player-row' },
{ field: 'ClubName', displayName: 'Mannschaft' },
{ field: 'DisplayPosition', displayName: 'Position', cellTemplate: playerPositionNameTemplate}
],
footerVisible: false
}">
</div>
On moving items from left to the right, I will push every item from SelectedItemsA into ItemsB via:
$.each(self.SelectedItemsA(), function(idx, player) {
self.ItemsB.push(player);
});
And cleaning the selection on the left side via:
self.ItemsA.removeAll(self.SelectedItemsA());
They items will appear correctly in the grid on the right side bounded to ItemsB, but they are automatically selected. So if I want to move a single item back, I first have to deselect all items I moved previously! How can I prevent kogrid from automatically selecting newly added items?
Its a bug in koGrid.
In koGrid-2.1.1.debug.js:
self.setRenderedRows = function (newRows) {
self.renderedRows(newRows);
self.refreshDomSizes();
};
newRows is an array of the rows you selected / copied.
koGrid copies them as they are, that means, that newRows.selected() (observable) is true.
UPDATE
Turns out, the above change would also de-select rows after they scrolled out of vision range. But i figured you can just set __kg_selected__ to false for each row you want to copy.
Say:
ko.utils.arrayForEach(self.SelectedItemsA(), function(player) {
player.__kg_selected__ = false;
});
and then push them all to the new array:
ko.utils.arrayPushAll(self.ItemsB(), self.SelectedItemsA());
So, I've got an implement of jqGrid which is working great. I'm displaying some rows of data and when new data is added -- the grid refreshes as appropriate.
However, if I try and drop a row from the grid -- it doesn't cleanup the missing rows! Adding new rows is fine, but deleting leaves data and bugs out the grid's display.
If I call 'clearGridData' first, I see the data cleaned up as appropriate. However, if I call clearGridData -- I lose my selection/page!
http://jsfiddle.net/yNw3C/1766/
var data = [[48803, "DSK1", "", "02200220", "OPEN"], [48769, "APPR", "", "77733337", "ENTERED"]];
$("#grid").jqGrid({
datatype: "local",
height: 250,
colNames: ['Inv No', 'Thingy', 'Blank', 'Number', 'Status'],
colModel: [{
name: 'id',
index: 'id',
width: 60,
sorttype: "int"},
{
name: 'thingy',
index: 'thingy',
width: 90,
sorttype: "date"},
{
name: 'blank',
index: 'blank',
width: 30},
{
name: 'number',
index: 'number',
width: 80,
sorttype: "float"},
{
name: 'status',
index: 'status',
width: 80,
sorttype: "float"}
],
caption: "Stack Overflow Example"
});
var names = ["id", "thingy", "blank", "number", "status"];
var mydata = [];
for (var i = 0; i < data.length; i++) {
mydata[i] = {};
for (var j = 0; j < data[i].length; j++) {
mydata[i][names[j]] = data[i][j];
}
}
for (var i = 0; i <= mydata.length; i++) {
$("#grid").jqGrid('addRowData', i + 1, mydata[i]);
}
$('#UpdateGridButton').click(function(){
mydata[0].status = "CLOSED";
delete mydata[1];
//If I add this -- I can refresh grid data properly, but I lose my selection.
//$('#grid').clearGridData();
$("#grid").jqGrid('setGridParam', { data: mydata});
$("#grid").trigger('reloadGrid', [{current: true}]);
});
Is the best of both worlds supported implicitly by jqGrid? Or do I need to write custom logic?
Sorry, but you use jqGrid in wrong way currently.
First of all the filling of the grid is very important. You use currently addRowData. It's the oldest, but the slowest way to fill the grid which I know. If you use datatype: "local" you should use data: mydata which allows to create directly jqGrid with the data.
Other very important options are gridview and rowNum. You should always use gridview: true to improve performance of grid (see the answer for more details). To understand the value of rowNum you should know that jqGrid is designed to support paging of data. In case of datatype: "local" sorting and paging will be implemented locally by jqGrid itself. The pager for navigation through pages will be created either by usage of toppager: true option or by usage pager: "#pagerId". Even if you don't create any pager local paging is still activated. Default value of rowNum is 20. So your current grid will display just the first 20 rows. the only exception if addRowData which force adding the row on the current page temporary. After reloading (for example indirectly by changing the sorting) only the first 20 rows will be shown for the user and the user will have no GUI to change the page (!!!???). If you want to display all rows on one page you should use rowNum: 10000 for example.
The next important parameter is autoencode: true which mean that the data for the cells should be interpreted as text and not as HTML fragment. If you don't use autoencode: true you will be unable to display the text which contain '<', '>', '&' and so on.
I recommend you additionally to use height: "auto" if you use local paging of data. It will remove empty space on the right size of the grid reserved for the scrollbar. You can use alternatively scrollOffset: 0 to remove the unneeded space.
The next important error in your code is the usage of setGridParam with data parameter. You cab do this only if the grid was empty before or if you need to add some additional rows. The setGridParam exist mostly for other purpose. It uses internally $.extend (see the code here). So you will extend the old contain of data with new values instead of replacing old data to new one. What you can do is for example to use getGridParam to get the reference to internal data parameter:
var data = $("#grid").jqGrid("getGridParam", "data");
Then you can modify array data using push, pop and delete. To full replace the data you can do
var allParameters = $("#grid").jqGrid("getGridParam");
allParameters.data = newData; // or newData.slice(0)
After that you can reload the grid by triggering of "reloadGrid".