Reputation: 176
I am using angular slickgrid to display my data and also for inline editing data. In table inline edit working fine but some exceptional cases are present. For multiselect editor the cell change callback not called while i am pressing ok button without changing the multiselect value its fine. but the edit view still maintaining the focus state. Any solution to remove the focus in the editor.
Current Behavior
Multiselect editor maintaining the focus while pressing ok button without changing data.
Expected Behavior
Multiselect editor field needs to remove the focus while pressing ok button without changing data.
Here i shared my code snippet for your reference
Html
<angular-slickgrid gridId="{{gridId}}" [columnDefinitions]="columnDefinitions" [gridOptions]="gridOptions"
[dataset]="filteredResultList" (sgOnClick)="onGridItemClick($event)"
(onAngularGridCreated)="angularGridReady($event)"
(sgOnCellChange)="onCellChanged($event.detail.eventData, $event.detail.args)">
</angular-slickgrid>
Grid options
public gridOptions: GridOption = {
enableAutoResize: true,
autoEdit: false,
autoCommitEdit: true,
enableCellNavigation: true,
editable: true,
enableSorting: true,
enableFiltering: true,
i18n: this.translateService,
enableExcelExport: true,
enableExport: true,
gridMenu: {
hideExportExcelCommand: true,
hideExportCsvCommand: true,
customItems: [{
command: "cspfm-excel-export",
titleKey: "EXPORT_TO_EXCEL",
iconCssClass: "fa fa-file-excel-o",
action: (event, callbackArgs) => {
this.excelExport(event, callbackArgs)
}
},
{
command: "cspfm-csv-export",
titleKey: "EXPORT_TO_CSV",
iconCssClass: "fa fa-download",
action: (event, callbackArgs) => {
this.excelExport(event, callbackArgs)
}
}
],
},
enableAutoTooltip: true,
autoTooltipOptions: {
enableForCells: true,
enableForHeaderCells: true,
maxToolTipLength: 1000
},
autoResize: {
containerId: this.gridContainerId,
calculateAvailableSizeBy: 'container'
},
exportOptions: {
exportWithFormatter: true
},
excelExportOptions: {
exportWithFormatter: true,
},
headerMenu: {
hideColumnHideCommand: true
},
enableTranslate: true,
presets: {
sorters: [{
columnId: this.tableColumnInfo['pfm187413']['pfm187413_name']['prop'],
direction: 'ASC'
}],
}
};
Table column info
public tableColumnInfo = {
"pfm187413": {
"pfm187413_name": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.name",
"fieldName": "name",
"prop": "name",
"fieldType": "TEXT",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_student": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.student",
"fieldName": "student",
"prop": "student",
"fieldType": "TEXT",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_about": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.about",
"fieldName": "about",
"prop": "about",
"fieldType": "TEXTAREA",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_qualification": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.qualification",
"fieldName": "qualification",
"prop": "qualification",
"fieldType": "DROPDOWN",
"child": "",
"dateFormat": "",
"mappingDetails": {
"ma": "MA",
"ba": "BA",
"bt": "BTECH",
"mca": "MCA"
},
"currencyDetails": ""
},
"pfm187413_dateofbirth": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.dateofbirth",
"fieldName": "dateofbirth",
"prop": "dateofbirth",
"fieldType": "DATE",
"child": "",
"dateFormat": this.appUtilityConfig.userDateFormat,
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_starttime": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.starttime",
"fieldName": "starttime",
"prop": "starttime",
"fieldType": "TIMESTAMP",
"child": "",
"dateFormat": this.appUtilityConfig.userDateTimeFormat,
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_gender": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.gender",
"fieldName": "gender",
"prop": "gender",
"fieldType": "RADIO",
"child": "",
"dateFormat": "",
"mappingDetails": {
"m": "Male",
"f": "Female"
},
"currencyDetails": ""
},
"pfm187413_favourites": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.favourites",
"fieldName": "favourites",
"prop": "favourites",
"fieldType": "MULTISELECT",
"child": "",
"dateFormat": "",
"mappingDetails": {
"ds": "Dosa",
"idli": "Idly",
"ps": "Pasta",
"sl": "Salad"
},
"currencyDetails": ""
},
"pfm187413_sports": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.sports",
"fieldName": "sports",
"prop": "sports",
"fieldType": "CHECKBOX",
"child": "",
"dateFormat": "",
"mappingDetails": {
"ck": "Cricket",
"ft": "Football",
"rg": "Rugby"
},
"currencyDetails": ""
},
"pfm187413_servicecost": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.servicecost",
"fieldName": "servicecost",
"prop": "servicecost",
"fieldType": "CURRENCY",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": {
"currencyCode": "$",
"display": true,
"digitsInfo": "1.2-2",
"locale": "ms-BN"
}
},
"pfm187413_cgapercent": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.cgapercent",
"fieldName": "cgapercent",
"prop": "cgapercent",
"fieldType": "DECIMAL",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_points": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.points",
"fieldName": "points",
"prop": "points",
"fieldType": "NUMBER",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_url": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.url",
"fieldName": "url",
"prop": "url",
"fieldType": "URL",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": ""
},
"pfm187413_isactive": {
"label": "LTFieldTrackSingleGLISTVA_WEB_Grid_with_List.Element.fieldtracksinglejuy.isactive",
"fieldName": "isactive",
"prop": "isactive",
"fieldType": "BOOLEAN",
"child": "",
"dateFormat": "",
"mappingDetails": "",
"currencyDetails": ""
}
}
};
Column definitions
public columnDefinitions: Array<Column> = [{
id: this.tableColumnInfo['pfm187413']['pfm187413_name']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_name']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_name']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
model: Filters.compoundInput
},
editor: {
model: Editors.longText,
required: true
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_name']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_student']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_student']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_student']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
model: Filters.compoundInput
},
editor: {
model: Editors.longText,
required: true
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_student']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_about']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_about']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_about']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
model: Filters.compoundInput
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_about']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_qualification']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_qualification']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_qualification']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
queryField: this.tableColumnInfo['pfm187413']['pfm187413_qualification']['prop'] + appConstant['customFieldSuffix']['slickgrid'],
filterable: true,
filter: {
collection: this.getLabelValue(this.qualification_371375),
model: Filters.multipleSelect
},
editor: {
model: Editors.singleSelect,
collection: this.getKeyValue(this.qualification_371375),
required: true
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_qualification']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_dateofbirth']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_dateofbirth']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_dateofbirth']['prop'],
sortable: true,
type: FieldType.date,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
queryField: this.tableColumnInfo['pfm187413']['pfm187413_dateofbirth']['prop'] + appConstant['customFieldSuffix']['slickgrid'],
filterable: true,
filter: {
operator: OperatorType.rangeInclusive,
model: Filters.compoundDate
},
editor: {
model: Editors.date,
required: true,
editorOptions:
{
onReady: (selectedDates, dateStr, instance) => {
const flatpickrYearElement = instance.currentYearElement;
const children = flatpickrYearElement.parentElement.children;
for (let i in children) {
if (children.hasOwnProperty(i)) {
children[i].style.display = 'none';
}
}
const yearSelect = document.createElement('select');
var minDate = new Date();
minDate.setHours(0, 0, 0, 0);
minDate.setFullYear(minDate.getFullYear() - 100);
var maxDate = new Date();
maxDate.setHours(23, 59, 58, 999);
maxDate.setFullYear(maxDate.getFullYear() + 50);
instance.config._minDate = minDate;
instance.config._maxDate = maxDate;
const minYear = new Date(instance.config._minDate).getFullYear();
const maxYear = new Date(instance.config._maxDate).getFullYear();
for (let i = minYear; i <= maxYear; i++) {
const option = document.createElement('option');
option.value = '' + i;
option.text = '' + i;
yearSelect.appendChild(option);
}
yearSelect.addEventListener('change', (event) => {
flatpickrYearElement.value = event.target['value'];
instance.changeYear(parseInt(event.target['value']));
});
yearSelect.className = 'flatpickr-monthDropdown-months';
yearSelect.id = 'flatpickr-custom-year-select';
yearSelect.value = instance.currentYearElement.value;
flatpickrYearElement.parentElement.appendChild(yearSelect);
},
onMonthChange: (selectedDates, dateStr, instance) => {
document.getElementById('flatpickr-custom-year-select')['value'] = '' + instance.currentYear;
}
} as FlatpickrOption
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_dateofbirth']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_starttime']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_starttime']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_starttime']['prop'],
sortable: true,
type: FieldType.dateTime,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
queryField: this.tableColumnInfo['pfm187413']['pfm187413_starttime']['prop'] + appConstant['customFieldSuffix']['slickgrid'],
filterable: true,
filter: {
operator: OperatorType.rangeInclusive,
model: Filters.compoundDate
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_starttime']
},
editor: {
model: Editors.date,
required: true,
editorOptions:
{
onReady: (selectedDates, dateStr, instance) => {
const flatpickrYearElement = instance.currentYearElement;
const children = flatpickrYearElement.parentElement.children;
for (let i in children) {
if (children.hasOwnProperty(i)) {
children[i].style.display = 'none';
}
}
const yearSelect = document.createElement('select');
var minDate = new Date();
minDate.setHours(0, 0, 0, 0);
minDate.setFullYear(minDate.getFullYear() - 100);
var maxDate = new Date();
maxDate.setHours(23, 59, 58, 999);
maxDate.setFullYear(maxDate.getFullYear() + 50);
instance.config._minDate = minDate;
instance.config._maxDate = maxDate;
const minYear = new Date(instance.config._minDate).getFullYear();
const maxYear = new Date(instance.config._maxDate).getFullYear();
for (let i = minYear; i <= maxYear; i++) {
const option = document.createElement('option');
option.value = '' + i;
option.text = '' + i;
yearSelect.appendChild(option);
}
yearSelect.addEventListener('change', (event) => {
flatpickrYearElement.value = event.target['value'];
instance.changeYear(parseInt(event.target['value']));
});
yearSelect.className = 'flatpickr-monthDropdown-months';
yearSelect.id = 'flatpickr-custom-year-select';
yearSelect.value = instance.currentYearElement.value;
flatpickrYearElement.parentElement.appendChild(yearSelect);
},
onMonthChange: (selectedDates, dateStr, instance) => {
document.getElementById('flatpickr-custom-year-select')['value'] = '' + instance.currentYear;
}
} as FlatpickrOption
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_gender']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_gender']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_gender']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
queryField: this.tableColumnInfo['pfm187413']['pfm187413_gender']['prop'] + appConstant['customFieldSuffix']['slickgrid'],
filterable: true,
filter: {
collection: this.getLabelValue(this.gender_371378),
model: Filters.multipleSelect
},
editor: {
model: Editors.singleSelect,
collection: this.getKeyValue(this.gender_371378),
required: true
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_gender']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_favourites']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_favourites']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_favourites']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
queryField: this.tableColumnInfo['pfm187413']['pfm187413_favourites']['prop'] + appConstant['customFieldSuffix']['slickgrid'],
filterable: true,
filter: {
collection: this.getLabelValue(this.favourites_371379),
operator: OperatorType.inContains,
model: Filters.multipleSelect
},
editor: {
model: Editors.multipleSelect,
collection: this.getKeyValue(this.favourites_371379)
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_favourites']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_sports']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_sports']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_sports']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
queryField: this.tableColumnInfo['pfm187413']['pfm187413_sports']['prop'] + appConstant['customFieldSuffix']['slickgrid'],
filterable: true,
filter: {
collection: this.getLabelValue(this.sports_371380),
operator: OperatorType.inContains,
model: Filters.multipleSelect
},
editor: {
model: Editors.multipleSelect,
collection: this.getKeyValue(this.sports_371380),
operator: OperatorType.inContains
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_sports']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_servicecost']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_servicecost']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_servicecost']['prop'],
sortable: true,
type: FieldType.number,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
model: Filters.compoundInputNumber
},
editor: {
model: Editors.float,
params: {
decimalPlaces: 2
},
required: true
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_servicecost']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_cgapercent']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_cgapercent']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_cgapercent']['prop'],
sortable: true,
type: FieldType.number,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
model: Filters.compoundInputNumber
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_cgapercent']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_points']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_points']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_points']['prop'],
sortable: true,
type: FieldType.number,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
model: Filters.compoundInputNumber
},
editor: {
model: cspfmCustomEditor,
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_points']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_url']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_url']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_url']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
model: Filters.compoundInput
},
editor: {
model: Editors.longText,
required: true
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_url']
}
}, {
id: this.tableColumnInfo['pfm187413']['pfm187413_isactive']['prop'],
nameKey: this.tableColumnInfo['pfm187413']['pfm187413_isactive']['label'],
field: this.tableColumnInfo['pfm187413']['pfm187413_isactive']['prop'],
sortable: true,
type: FieldType.string,
exportCustomFormatter: CspfmDataFormatter,
minWidth: this.columnMinWidth,
formatter: CspfmDataFormatter,
filterable: true,
filter: {
collection: this.getLabelValue({
true: "true",
false: "false"
}),
model: Filters.multipleSelect
},
editor: {
model: Editors.checkbox,
collection: [{
value: true,
label: 'True'
},
{
valued: false,
label: 'False'
}
]
},
params: {
pipe: this.cspfmDataDisplay,
fieldInfo: this.tableColumnInfo['pfm187413']['pfm187413_isactive']
}
}];
Behavior of angular slickgrid sample project
Software Version
Angular : 7.3.5
Angular-Slickgrid : 2.19.0
TypeScript : 3.1.6
Operating System : Windows 10
Node : 10.16.3
NPM : 6.9.0
Upvotes: 2
Views: 1101
Reputation: 13194
You need the Grid Option autoCommitEdit: true
to commit the change right away, which will push the change, close the editor and so lose focus from it. Doing that will also trigger an onCellChange
event right away (while having autoCommitEdit: false
won't trigger until you tab away or blur away).
The default is autoCommitEdit: false
which has the behavior that you mentioned and is normal. You need to tab away or focus on another cell. I personally always use autoCommitEdit: true
to avoid such behavior. The behavior you mentioned is considered "As Per Designed" and won't change.
Also note that using autoCommitEdit: false
on a Text Editor will go down to the next available row while autoCommitEdit: true
will simply commit only the current cell and not open any other editor. That is again a behavior that I don't really like but some user might.
So the quick answer is use autoCommitEdit: true
NOTE
Please don't override the onClose
method of the Select Editor, if you are then you are completely overriding the lib with its own onClose
usage here. The autoCommitEdit
is only used and effective within the onClose
(when that triggers it calls the internal code save which checks the autoCommitEdit
flag at that time) and so if you are overriding the onClose
then this flag as no effect whatsoever.
EDIT 1
I cannot replicate your issue on the demo... actually that is no longer true, I misunderstood the original question. The behavior that is problematic and is asked in the question is from SlickGrid (core lib)
EDIT 2
You can probably fix your problem by adding an event to the body
and once click then run the Editors commit. Something like
document.body.addEventListener('click', () => {
// auto-commit any opened editor, that will close the editor and commit the changes
if (this.grid.getEditorLock()) {
this.grid.getEditorLock().commitCurrentEdit();
}
});
Upvotes: 0