Cleanup handling of default/null values when data editting. FIxes #2400

This commit is contained in:
Surinder Kumar
2017-05-27 14:51:02 -04:00
committed by Dave Page
parent 10caae4dc6
commit 1f26953504
3 changed files with 204 additions and 111 deletions

View File

@@ -11,17 +11,61 @@
"Editors": { "Editors": {
"pgText": pgTextEditor, "pgText": pgTextEditor,
"JsonText": JsonTextEditor, "JsonText": JsonTextEditor,
"CustomNumber": CustomNumberEditor,
// Below editor will read only editors, Just to display data // Below editor will read only editors, Just to display data
"ReadOnlyText": ReadOnlyTextEditor, "ReadOnlyText": ReadOnlyTextEditor,
"ReadOnlyCheckbox": ReadOnlyCheckboxEditor, "ReadOnlyCheckbox": ReadOnlyCheckboxEditor,
"Checkbox": CheckboxEditor, // Override editor to implement checkbox with three states "Checkbox": CheckboxEditor, // Override editor to implement checkbox with three states
"ReadOnlypgText": ReadOnlypgTextEditor, "ReadOnlypgText": ReadOnlypgTextEditor,
"ReadOnlyJsonText": ReadOnlyJsonTextEditor, "ReadOnlyJsonText": ReadOnlyJsonTextEditor
"CustomNumber": CustomNumberEditor
} }
} }
}); });
/*
* This function handles the [default] and [null] values for cells
* if row is copied, otherwise returns the editor value.
* @param {args} editor object
* @param {item} row cell values
* @param {state} entered value
* @param {column_type} type of column
*/
function setValue(args, item, state, column_type) {
// declare a 2-d array which tracks the status of each updated cell
// If a cell is edited for the 1st time and state is null,
// set cell value to [default] and update its status [row][cell] to 1.
// If same cell is edited again, and kept blank, set cell value to [null]
// If a row is copied
var grid = args.grid;
if (item.is_row_copied) {
if (!grid.copied_rows) {
grid.copied_rows = [[]];
}
var active_cell = grid.getActiveCell(),
row = active_cell['row'],
cell = active_cell['cell'],
last_value = item[args.column.pos],
last_value = (column_type === 'number') ?
(_.isEmpty(last_value) || last_value) : last_value;
item[args.column.pos] = state;
if (last_value && _.isNull(state) &&
(_.isUndefined(grid.copied_rows[row]) ||
_.isUndefined(grid.copied_rows[row][cell]))
) {
item[args.column.pos] = undefined;
if (grid.copied_rows[row] == undefined) grid.copied_rows[row] = [];
grid.copied_rows[row][cell] = 1;
}
}
else {
item[args.column.pos] = state;
}
}
// Text data type editor // Text data type editor
function pgTextEditor(args) { function pgTextEditor(args) {
var $input, $wrapper; var $input, $wrapper;
@@ -113,10 +157,10 @@
var col = args.column; var col = args.column;
if (_.isUndefined(item[args.column.pos]) && col.has_default_val) { if (_.isUndefined(item[args.column.pos]) && col.has_default_val) {
$input.val(""); $input.val(defaultValue = "");
} }
else if (item[args.column.pos] === "") { else if (item[args.column.pos] === "") {
$input.val("''"); $input.val(defaultValue = "''");
} }
else { else {
$input.val(defaultValue = item[args.column.pos]); $input.val(defaultValue = item[args.column.pos]);
@@ -146,7 +190,7 @@
}; };
this.applyValue = function (item, state) { this.applyValue = function (item, state) {
item[args.column.pos] = state; setValue(args, item, state, 'text');
}; };
this.isValueChanged = function () { this.isValueChanged = function () {
@@ -290,7 +334,7 @@
}; };
this.applyValue = function (item, state) { this.applyValue = function (item, state) {
item[args.column.pos] = state; setValue(args, item, state, 'text');
}; };
this.isValueChanged = function () { this.isValueChanged = function () {
@@ -855,7 +899,7 @@
}; };
this.applyValue = function (item, state) { this.applyValue = function (item, state) {
item[args.column.pos] = state; setValue(args, item, state, 'number');
}; };
this.isValueChanged = function () { this.isValueChanged = function () {

View File

@@ -463,11 +463,16 @@ class TableCommand(GridCommand):
column_data[each_col] = None column_data[each_col] = None
column_type[each_col] =\ column_type[each_col] =\
self.columns_info[each_col]['type_name'] self.columns_info[each_col]['type_name']
else:
column_type[each_col] = \
self.columns_info[each_col]['type_name']
for each_row in changed_data[of_type]: for each_row in changed_data[of_type]:
data = changed_data[of_type][each_row]['data'] data = changed_data[of_type][each_row]['data']
# Remove our unique tracking key # Remove our unique tracking key
data.pop('__temp_PK', None) data.pop('__temp_PK', None)
data.pop('is_row_copied', None)
data = set_column_names(data) data = set_column_names(data)
data_type = set_column_names(changed_data[of_type][each_row]['data_type']) data_type = set_column_names(changed_data[of_type][each_row]['data_type'])
list_of_rowid.append(data.get('__temp_PK')) list_of_rowid.append(data.get('__temp_PK'))

View File

@@ -39,10 +39,27 @@ define(
pgBrowser = pgAdmin.Browser, pgBrowser = pgAdmin.Browser,
Slick = window.Slick; Slick = window.Slick;
/* Get the function definition from /* Reference link
* http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript/35302975#35302975 * http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
* Modified as per requirement.
*/ */
function epicRandomString(b){for(var a=(Math.random()*eval("1e"+~~(50*Math.random()+50))).toString(36).split(""),c=3;c<a.length;c++)c==~~(Math.random()*c)+1&&a[c].match(/[a-z]/)&&(a[c]=a[c].toUpperCase());a=a.join("");a=a.substr(~~(Math.random()*~~(a.length/3)),~~(Math.random()*(a.length-~~(a.length/3*2)+1))+~~(a.length/3*2));if(24>b)return b?a.substr(a,b):a;a=a.substr(a,b);if(a.length==b)return a;for(;a.length<b;)a+=epicRandomString();return a.substr(0,b)}; function epicRandomString(b) {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(
Math.floor(Math.random() * 0x10), 1
);
}
// bits 12-15 of the time_hi_and_version field to 0010
s[14] = "4";
// bits 6-7 of the clock_seq_hi_and_reserved to 01
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid.replace(/-/g, '').substr(0, b);
};
// Define key codes for shortcut keys // Define key codes for shortcut keys
var F5_KEY = 116, var F5_KEY = 116,
@@ -526,6 +543,21 @@ define(
render_grid: function(collection, columns, is_editable) { render_grid: function(collection, columns, is_editable) {
var self = this; var self = this;
// returns primary keys
self.handler.get_row_primary_key = function() {
var self = this,
tmp_keys = [];
_.each(self.primary_keys, function(p, idx) {
// For each columns search primary key position
_.each(self.columns, function(c) {
if(c.name == idx) {
tmp_keys.push(c.pos);
}
});
});
return tmp_keys;
};
// This will work as data store and holds all the // This will work as data store and holds all the
// inserted/updated/deleted data from grid // inserted/updated/deleted data from grid
self.handler.data_store = { self.handler.data_store = {
@@ -666,7 +698,8 @@ define(
primary_key_list = _.keys(this.keys), primary_key_list = _.keys(this.keys),
_tmp_keys = [], _tmp_keys = [],
_columns = this.columns, _columns = this.columns,
rows_for_stage = {}, selected_rows_list = []; rows_for_stage = {},
selected_rows_list = [];
// Only if entire row(s) are selected via check box // Only if entire row(s) are selected via check box
if(_.has(this.selection, 'getSelectedRows')) { if(_.has(this.selection, 'getSelectedRows')) {
@@ -686,14 +719,19 @@ define(
// Check if selected is new row ? // Check if selected is new row ?
// Allow to delete if yes // Allow to delete if yes
var cell_el = this.grid.getCellNode(selected_rows_list[0], 0), var count = selected_rows_list.length-1,
cell_el = this.grid.getCellNode(selected_rows_list[count],0),
parent_el = $(cell_el).parent(), parent_el = $(cell_el).parent(),
is_new_row = $(parent_el).hasClass('new_row'); is_new_row = $(parent_el).hasClass('new_row');
// Clear selection model if row primary keys is set to default // Clear selection model if row primary keys is set to default
var row_data = collection[selected_rows_list[0]]; var row_data = _.clone(collection[selected_rows_list[count]]),
is_primary_key = _.has(row_data, primary_key_list) &&
row_data[0] != undefined ? true : false;
if (primary_key_list.length && if (primary_key_list.length &&
!_.has(row_data, primary_key_list) && !is_new_row) { !is_primary_key && !is_new_row
) {
this.selection.setSelectedRows([]); this.selection.setSelectedRows([]);
selected_rows_list = []; selected_rows_list = [];
} }
@@ -716,22 +754,20 @@ define(
// Collect primary key data from collection as needed for stage row // Collect primary key data from collection as needed for stage row
_.each(selected_rows_list, function(row_index) { _.each(selected_rows_list, function(row_index) {
var row_data = collection[row_index], var row_data = collection[row_index],
pkey_data = _.pick(row_data, primary_key_list); p_keys_list = _.pick(row_data, primary_key_list),
is_primary_key = Object.keys(p_keys_list).length ?
p_keys_list[0] : undefined;
// Store Primary key data for selected rows // Store Primary key data for selected rows
if (!_.isUndefined(row_data) && !_.isUndefined(pkey_data)) { if (!_.isUndefined(row_data) && !_.isUndefined(p_keys_list)) {
// check for invalid row // check for invalid row
rows_for_stage[row_data.__temp_PK] = _.pick(row_data, primary_key_list); rows_for_stage[row_data.__temp_PK] = p_keys_list;
} }
}); });
} else { } else {
//clear staged rows //clear staged rows
clear_staged_rows(); clear_staged_rows();
} }
if (!Object.keys(rows_for_stage).length) {
clear_staged_rows();
}
// Update main data store // Update main data store
this.editor.handler.data_store.staged_rows = rows_for_stage; this.editor.handler.data_store.staged_rows = rows_for_stage;
}.bind(editor_data)); }.bind(editor_data));
@@ -816,7 +852,27 @@ define(
column_data = {}, column_data = {},
_type; _type;
column_data[changed_column] = updated_data; // Access to row/cell value after a cell is changed.
// The purpose is to remove row_id from temp_new_row
// if new row has primary key instead of [default_value]
// so that cell edit is enabled for that row.
var grid = args.grid,
row_data = grid.getDataItem(args.row),
p_keys_list = _.pick(
row_data, self.handler.get_row_primary_key()
),
is_primary_key = Object.keys(p_keys_list).length ?
p_keys_list[0] : undefined;
// temp_new_rows is available only for view data.
if (is_primary_key && self.handler.temp_new_rows) {
var index = self.handler.temp_new_rows.indexOf(args.row);
if (index > -1) {
self.handler.temp_new_rows.splice(index, 1);
}
}
column_data[changed_column] = updated_data;
if(_pk) { if(_pk) {
// Check if it is in newly added row by user? // Check if it is in newly added row by user?
@@ -854,53 +910,42 @@ define(
$("#btn-save").prop('disabled', false); $("#btn-save").prop('disabled', false);
}.bind(editor_data)); }.bind(editor_data));
// Listener function which will be called after cell is changed
grid.onActiveCellChanged.subscribe(function (e, args) {
// Access to row/cell value after a cell is changed.
// The purpose is to remove row_id from temp_new_row
// if new row has primary key instead of [default_value]
// so that cell edit is enabled for that row.
var grid = args.grid,
row_data = grid.getDataItem(args.row),
primary_key = row_data && row_data[0];
// temp_new_rows is available only for view data.
if (!_.isUndefined(primary_key) &&
self.handler.temp_new_rows
) {
var index = self.handler.temp_new_rows.indexOf(args.row);
if (index > -1) {
self.handler.temp_new_rows.splice(index, 1);
}
}
});
// Listener function which will be called when user adds new rows // Listener function which will be called when user adds new rows
grid.onAddNewRow.subscribe(function (e, args) { grid.onAddNewRow.subscribe(function (e, args) {
// self.handler.data_store.added will holds all the newly added rows/data // self.handler.data_store.added will holds all the newly added rows/data
var _key = epicRandomString(10), var _key = epicRandomString(10),
column = args.column, column = args.column,
item = args.item, data_length = this.grid.getDataLength(); item = args.item,
data_length = this.grid.getDataLength(),
new_collection = args.grid.getData();
// Add new row in list to keep track of it // Add new row in list to keep track of it
if (_.isUndefined(item[0])) { if (_.isUndefined(item[0])) {
self.handler.temp_new_rows.push(data_length); self.handler.temp_new_rows.push(data_length);
} }
// If copied item has already primary key, use it.
if(item) { if(item) {
item.__temp_PK = _key; item.__temp_PK = _key;
} }
collection.push(item); new_collection.push(item);
self.handler.data_store.added[_key] = {'err': false, 'data': item}; self.handler.data_store.added[_key] = {'err': false, 'data': item};
self.handler.data_store.added_index[data_length] = _key; self.handler.data_store.added_index[data_length] = _key;
// Fetch data type & add it for the column // Fetch data type & add it for the column
var temp = {}; var temp = {};
temp[column.pos] = _.where(this.columns, {pos: column.pos})[0]['type']; temp[column.pos] = _.where(this.columns, {pos: column.pos})[0]['type'];
self.handler.data_store.added[_key]['data_type'] = temp; self.handler.data_store.added[_key]['data_type'] = temp;
grid.invalidateRows([collection.length - 1]); grid.invalidateRows([new_collection.length - 1]);
grid.updateRowCount(); grid.updateRowCount();
grid.render(); grid.render();
// Add a blank row after add row
grid.setData(new_collection, true);
grid.updateRowCount();
grid.invalidateAllRows();
grid.render();
// Enable save button // Enable save button
$("#btn-save").prop('disabled', false); $("#btn-save").prop('disabled', false);
}.bind(editor_data)); }.bind(editor_data));
@@ -2249,22 +2294,17 @@ define(
}, },
rows_to_delete: function(data) { rows_to_delete: function(data) {
var self = this; var self = this,
var tmp_keys = []; tmp_keys = self.get_row_primary_key.call(self);
_.each(self.primary_keys, function(p, idx) {
// For each columns search primary key position
_.each(self.columns, function(c) {
if(c.name == idx) {
tmp_keys.push(c.pos);
}
});
});
// re-calculate rows with no primary keys // re-calculate rows with no primary keys
self.temp_new_rows = []; self.temp_new_rows = [];
data.forEach(function(d, idx) { data.forEach(function(d, idx) {
var p_keys_idx = _.pick(d, tmp_keys); var p_keys_list = _.pick(d, tmp_keys),
if (Object.keys(p_keys_idx).length == 0) { is_primary_key = Object.keys(p_keys_list).length ?
p_keys_list[0] : undefined;
if (!is_primary_key) {
self.temp_new_rows.push(idx); self.temp_new_rows.push(idx);
} }
}); });
@@ -2381,7 +2421,7 @@ define(
is_primary_error = false; is_primary_error = false;
if( !is_added && !is_updated && !is_deleted ) { if( !is_added && !is_updated && !is_deleted ) {
return; // Nothing to save here return; // Nothing to save here
} }
if (save_data) { if (save_data) {
@@ -2405,6 +2445,18 @@ define(
var grid = self.slickgrid, var grid = self.slickgrid,
data = grid.getData(); data = grid.getData();
if (res.data.status) { if (res.data.status) {
// Remove flag is_row_copied from copied rows
_.each(data, function(row, idx) {
if (row.is_row_copied) {
delete row.is_row_copied;
}
});
// Remove 2d copied_rows array
if (grid.copied_rows) {
delete grid.copied_rows;
}
// Remove deleted rows from client as well // Remove deleted rows from client as well
if(is_deleted) { if(is_deleted) {
var rows = grid.getSelectedRows(); var rows = grid.getSelectedRows();
@@ -3071,67 +3123,59 @@ define(
_paste_row: function() { _paste_row: function() {
var self = this, col_info = {}, var self = this, col_info = {},
grid = self.slickgrid, grid = self.slickgrid,
data = grid.getData(); data = grid.getData(),
// Deep copy count = Object.keys(data).length-1;
var copied_rows = $.extend(true, [], self.copied_rows),
_tmp_copied_row = {}; var rows = grid.getSelectedRows().sort(
function (a, b) { return a - b; }
),
rows = rows.length == 0 ? self.last_copied_rows : rows,
copied_rows = rows.map(function (rowIndex) {
return data[rowIndex];
});
self.last_copied_rows = rows;
// If there are rows to paste? // If there are rows to paste?
if(copied_rows.length > 0) { if(copied_rows.length > 0) {
// Enable save button so that user can // Enable save button so that user can
// save newly pasted rows on server // save newly pasted rows on server
$("#btn-save").prop('disabled', false); $("#btn-save").prop('disabled', false);
// Generate Unique key for each pasted row(s)
_.each(copied_rows, function(row) {
var _pk = epicRandomString(8);
row.__temp_PK = _pk;
});
var temp_func = self.data_view.getItemMetadata, var arr_to_object = function (arr) {
count = Object.keys(data).length-1; var obj = {},
count = typeof(arr) == 'object' ?
Object.keys(arr).length: arr.length
_.each(copied_rows, function(row, idx) { _.each(arr, function(val, i){
data[count] = row; if (arr[i] !== undefined) {
count++; if(_.isObject(arr[i])) {
}); obj[String(i)] = JSON.stringify(arr[i]);
} else {
//update data_view obj[String(i)] = arr[i];
data.getItemMetadata = temp_func; }
grid.setData(data, true);
grid.updateRowCount();
grid.setSelectedRows([]);
grid.invalidateAllRows();
grid.render();
// Fetch column name & its data type
_.each(self.columns, function(c) {
col_info[String(c.pos)] = c.type;
});
// insert these data in data_store as well to save them on server
for (var j = 0; j < copied_rows.length; j += 1) {
self.data_store.added[copied_rows[j].__temp_PK] = {
'data_type': {},
'data': {}
};
self.data_store.added[copied_rows[j].__temp_PK]['data_type'] = col_info;
// We need to convert it from array to dict so that server can
// understand the data properly
_.each(copied_rows[j], function(val, key) {
// If value is array then convert it to string
if(_.isArray(val)) {
_tmp_copied_row[String(key)] = val.toString();
// If value is object then stringify it
} else if(_.isObject(val)) {
_tmp_copied_row[j][String(key)] = JSON.stringify(val);
} else {
_tmp_copied_row[String(key)] = val;
} }
}); });
self.data_store.added[copied_rows[j].__temp_PK]['data'] = _tmp_copied_row; return obj;
// reset the variable };
_tmp_copied_row = {};
} // Generate Unique key for each pasted row(s)
// Convert array values to object to send to server
// Add flag is_row_copied to handle [default] and [null]
// for copied rows.
// Add index of copied row into temp_new_rows
// Trigger grid.onAddNewRow when a row is copied
// Reset selection
_.each(copied_rows, function(row) {
var new_row = arr_to_object(row);
new_row.is_row_copied = true;
row = new_row;
self.temp_new_rows.push(count);
grid.onAddNewRow.notify(
{item: new_row, column: self.columns[0] , grid:grid}
)
grid.setSelectedRows([]);
count++;
});
} }
}, },