making certain cells of an ExtJS GridPanel un-editable - javascript

I currently have a GridPanel with the Ext.ux.RowEditor plugin. Four fields exist in the row editor: port, ip address, subnet and DHCP. If the DHCP field (checkbox) of the selected row is checked, I need to make the other three fields un-editable.
I've been trying to perform this code when the beforeedit event is triggered, but to no avail... I've only found ways to make the entire column un-editable. My code so far:
this.rowEditor.on({
scope: this,
beforeedit: this.checkIfEditable
});
checkIfEditable:function(rowEditor, rowIndex) {
if(this.getStore().getAt(rowIndex).get('dhcp')) {
// this function makes the entire column un-editable:
this.getColumnModel().setEditable(2, false);
// I want to make only the other three fields of the current row
// uneditable.
}
}
Please let me know if any clarification is needed.
Any help potentially extending RowEditor to accomplish the target functionality would be greatly appreciated as well!

You can add into RowEditor.js within the function startEditing the call to the function isCellEditable
//.... RowEditor.js
startEditing: function(rowIndex, doFocus){
//.... search for the starting of the below loop into the source code
var cm = g.getColumnModel(), fields = this.items.items, f, val;
for(var i = 0, len = cm.getColumnCount(); i < len; i++){
val = this.preEditValue(record, cm.getDataIndex(i));
f = fields[i];
f.setValue(val);
// *** here add a call to isCellEditable *** //
f.setDisabled(!cm.isCellEditable(i, rowIndex));
// ***************************************** //
this.values[f.id] = Ext.isEmpty(val) ? '' : val;
}
//....
Then override the isCellEditable of your column model and disabled the field based on you condition:
YYY.getColumnModel().isCellEditable = function(colIndex, rowIndex){
if (grid.getStore().getAt(rowIndex).get('dhcp')) {
// you can do more check base on colIndex
// only to disabled the one you want
return false;
}
return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, colIndex, rowIndex);
}

As the docs state:
If the listener returns false
the editor will not be activated.
So...
this.rowEditor.on({
scope: this,
beforeedit: this.checkIfEditable
});
checkIfEditable:function(rowEditor, rowIndex) {
if(this.getStore().getAt(rowIndex).get('dhcp')) {
return false;
}
}
Simply returning false will be enough to cancel the editing ability.
Gotcha.
Interesting idea - a bit of a hassle to implement, but possible.
You need to approach this from two directions:
1 ) edit starts
2 ) checkbox is checked/unchecked
For the first part, I think you could use almost the same code I have above, remove the 'return false' and use the reference to the rowEditor to loop through the items collection, disabling (call the disable method on them) the fields that are not your checkbox field.
The second part of this is to add a handler to the checkbox which would do the same thing.

The following works to make a specific cell uneditable (add the event to the roweditor):
beforeedit: function (roweditor, rowIndex) {
var data = roweditor.grid.store.getAt(rowIndex).data;
var cm = roweditor.grid.getColumnModel();
// disable the first column (can not be edited)
if (** make your check here ***) {
var c = cm.getColumnAt(0);
c.editor.disable();
}
roweditor.initFields();
}

As of ExtJS 3.4 you can just override isCellEditable, as in the example here:
http://docs.sencha.com/ext-js/3-4/#!/api/Ext.grid.ColumnModel-method-isCellEditable

Here's the simpler version :
http://jsfiddle.net/snehalmasne/8twwywcp/
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
,pluginId: 'editing'
})
]
Provide the condition below for the cells to make the un-editable :
grid.on('beforeedit', function(editor, e) {
if (e.record.get('phone') == '555-111-1224')
return false;
});

just set the column in your columnModel/columns to editable:false for fields that should not be editable.
columns: [
{ header: "Ticker", width: 60, editable:false },
{ header: "Company Name", width: 150, id: 'company'},
{ header: "Market Cap."},
{ header: "$ Sales", renderer: money},
{ header: "Employees", resizable: false}
]

I ran into the same problem. Rather than using the GridPanel with the Ext.ux.RowEditor plugin, I used the Ext.grid.EditorGridPanel. In this case, you can specify the editor for each of the other three fields (port, ip address, and subnet) in the column model with a beforeshow event handler as follows:
editor: new Ext.form.TextArea({
height:80,
allowBlank: false,
listeners:{
beforeshow: function(column) {
if (column.gridEditor.record.get('dhcp')) {
column.gridEditor.cancelEdit();
}
}
}
})

Ha!
I made a dirty one, I added an event this.fireEvent('starting', this, fields,record); AT THE END of startEditing
startEditing: function(rowIndex, doFocus){
if(this.editing && this.isDirty()){
this.showTooltip(this.commitChangesText);
return;
}
if(Ext.isObject(rowIndex)){
rowIndex = this.grid.getStore().indexOf(rowIndex);
}
if(this.fireEvent('beforeedit', this, rowIndex) !== false){
this.editing = true;
var g = this.grid, view = g.getView(),
row = view.getRow(rowIndex),
record = g.store.getAt(rowIndex);
this.record = record;
this.rowIndex = rowIndex;
this.values = {};
if(!this.rendered){
this.render(view.getEditorParent());
}
var w = Ext.fly(row).getWidth();
this.setSize(w);
if(!this.initialized){
this.initFields();
}
var cm = g.getColumnModel(), fields = this.items.items, f, val;
for(var i = 0, len = cm.getColumnCount(); i < len; i++){
val = this.preEditValue(record, cm.getDataIndex(i));
f = fields[i];
f.setValue(val);
this.values[f.id] = Ext.isEmpty(val) ? '' : val;
}
this.verifyLayout(true);
if(!this.isVisible()){
this.setPagePosition(Ext.fly(row).getXY());
} else{
this.el.setXY(Ext.fly(row).getXY(), {duration:0.15});
}
if(!this.isVisible()){
this.show().doLayout();
}
if(doFocus !== false){
this.doFocus.defer(this.focusDelay, this);
}
/*I ADDED THIS EVENT ---- contains the fields and record
*/
this.fireEvent('starting', this, fields,record);
}
}
THEN
var editor = new Ext.ux.grid.RowEditor({
saveText: 'Update',
listeners : {
'starting' : function(rowEditor, fields, record){
if(!record.data.equipo_id){
fields[4].setDisabled(false);
}else{
fields[4].setDisabled(true);
}
},

Using Ext JS 4 and the RowEditing plugin i managed to achieve this using something like
rowEditor.on('beforeedit', function (context) {
this.editor.query('textfield')[0].setDisabled(/* your condition here */);
});
the record data is available through context.record.data

Related

Ag-grid highlight cell logic not working properly

I created an editable grid using ag-grid and I need to highlight the cells that were changed. I added the following code:
(cellValueChanged)="onDemandsCellValueChanged($event)"
And the method:
onDemandsCellValueChanged(params): void {
if (params.oldValue === params.newValue) {
return;
}
params.data.modified = true; // add modified property so it can be filtered on save
const column = params.column.colDef.field;
params.column.colDef.cellStyle = { 'background-color': '#e1f9e8' }; // highlight modified cells
params.api.refreshCells({
force: true,
columns: [column],
rowNodes: [params.node],
});
}
The cell is highlighted but when I scroll up and down all the cell from that column are highlighted.
You can check the behavior on stackblitz.
Also if you have a better way of doing this I'm open to new solutions.
I understand your problem You can achieve what you want like this - you should use cellStyle in your column definition as showing in docs and for this code is below
cellStyle: params => {
if (
params.data["modifiedRow" +
params.node.rowIndex +"andCellKey"+
params.column.colDef.field]
) {
return { backgroundColor: "#e1f9e8" };
} else {
return null;
}
},
and after that in this function onDemandsCellValueChanged please add and modify this property modified as true and change your function like this as shown below
onDemandsCellValueChanged(params): void {
if (params.oldValue === params.newValue) {
return;
}
const column = params.column.colDef.field;
params.data["modifiedRow" + params.rowIndex + "andCellKey" + column] = true;
params.api.refreshCells({
force: true,
columns: [column],
rowNodes: [params.node]
});
}
Now it should work even when you scroll. Here is complete working Example on stackblitz

Appcelerator - Programmatic Toggle of Switch in Custom TableViewRow

I have a custom TableViewRow with a title and switch like below:
rowFilter.xml
<TableViewRow id="rowFilter">
<View id="vwItemHeader">
<Label id="lblItemHeader"></Label>
</View>
<View id="vwFilterStatus">
<Switch id="swtFilterStatus" onChange="swtFilterStatusChange"></Switch>
</View>
</TableViewRow>
rowFilter.js
var args = arguments[0] || {};
var swtFilterStatusChange_callback;
initialize();
function initialize() {
// Initialize filter row UI
$.lblItemHeader.text = args.title;
$.swtFilterStatus.value = args.value;
// Callback
swtFilterStatusChange_callback = args.callback;
};
In a view I call Browse, I programmatically add these custom rows as follows:
var args = { title: item.title, value: item.value, callback: swtFilterStatusChanged, };
var newRow = Alloy.createController('rowFilter', args).getView('rowFilter');
This works just as intended. However now I want to add a check/uncheck all row. How do I programmatically toggle a switch within my custom rows?
I've tried creating the following function in rowFilter.js (and a similar one for toggle off):
exports.toggleOn = function() {
if ($.swtFilterStatus.value == false) {
$.swtFilterStatus.value = true;
swtFilterStatusChange();
}
};
And I've also tried this:
$.toggleOn = function() {
}
::EDIT:: Here is how I handle the check/uncheck all switch.
function allSwitch_Change(value) {
$.tblFilters.data[0].rows.forEach(function(row) {
if (value) {
row.toggleOn();
}
else {
row.toggleOf();
}
}
}
Then changing the Browse.js row above that does Alloy.createController with the following:
var newRow = require('rowFilter');
newRow.initialize(args);
But I just get an exception on the line with the require statement saying "Couldn't find module:rowFilter for architecture: x86_64".
What am I doing wrong and how do I implement a check/uncheck all row?
You can do something like this:
rowFilter.js
var args = arguments[0] || {};
var swtFilterStatusChange;
initialize();
function initialize() {
// Initialize filter row UI
$.lblItemHeader.text = args.title;
$.swtFilterStatus.value = args.value;
// Callback
swtFilterStatusChange = args.callback;
};
$.on('toggleOn',function(){
if ($.swtFilterStatus.value == false) {
$.swtFilterStatus.value = true;
swtFilterStatusChange();
}
});
browser.js
var args = { title: item.title, value: item.value, callback: swtFilterStatusChanged, };
var newRow = Alloy.createController('rowFilter', args);
newRow.trigger('toggleOn');
Other options to achieve your goal:
Global Events - Ti.App.addEventListener and Ti.App.fireEvent. Easy to implement, but has potential memory leak. Make sure to call Ti.App.removeEventListener after table row removed.
Get the switch view from the table row's children. Manually fire the event on it.
Store the reference to the callback swtFilterStatusChanged somewhere and call it later.

ExtJS 4.2 - Tab Selects Wrong Value in Combobox with typeAhead:true when Typing Fast

When a combobox is used in ExtJS (tested in 4.2, but likely other versions as well), and the "typeAhead: true" option is set, if you the user types in values very quickly and then hits the "tab" on their keyboard, the focus will move to the next field on the screen and the wrong value is set. Because of the tricky nature of this bug, I have created a JSFiddle for it here: http://jsfiddle.net/59AVC/2/
To replicate the bug, very quickly type "975" and then "tab" in the first combobox field. If you hit tab very quickly after you enter the "5" in "975", you will see that the combobox is set to the "970" option instead. I believe this is happening because the "Tab" is causing whatever option is highlighted in the list to be the value that is set, but what is strange is that the "970" is highlighted still after the "5" in "975" is entered, when it should process that event first before the "tab" and it should have changed the selection to be the correct "975".
I have tried adjusting the typeAheadDelay (set to 0 in the example), as well as the queryDelay and everything else I can think of. It looks like ExtJS is simply canceling the lookup that is somehow still running and not finished when the tab is pressed.
Any suggestions on how to work around this bug? Do I need to write my own "typeAhead" auto-complete function to handle this correctly by single threading the events?
Here is the sample JSFiddle code that shows this:
// The data store containing the list of values
var states = Ext.create('Ext.data.Store', {
fields: ['val_number', 'val_name'],
data : [
{"val_number":"970", "val_name":"970 - Name"},
{"val_number":"971", "val_name":"971 - Name"},
{"val_number":"972", "val_name":"972 - Name"},
{"val_number":"973", "val_name":"973 - Name"},
{"val_number":"974", "val_name":"974 - Name"},
{"val_number":"975", "val_name":"975 - Name"}
//...
]
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 1st Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 2nd Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
UPDATED:
Tried this code as suggested, no change in result - still doesn't select correctly:
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
console.log('onTypeAhead...');
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
});
found the problematic code snippet:
beforeBlur: function() {
this.doQueryTask.cancel();
this.assertValue();
},
the problem is not typeAhead its selectOnTab together with autoSelect which will be set to true from typeahead.
so this happens:
you type "97" a query will fire and the first value (970) will be selected
you type "5" a query will start, but
you press "tab" and the beforeBlur function gets executed.
the query gets canceled and the currently highlighted value becomes the field value
so what can you do?
it is not possible to give the task a callback so assertValue is called after the query finishes :(
you need to disable autoselect again and the only way i found is to override onTypeAhead and to comment out the highlighting:
.
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
Thanks to Jandalf, I have some good news. I was able to work out a solution for my needs by extending the combobox and introducing a few fixes. The first was to do as Jandalf suggested (a good starting point) and the next set of fixes was to stop using a DelayedTask if the delay was 0 or less (my config sets "typeAheadDelay" and "queryDelay" to 0). Finally, I had to also do a check in the "assertValue" that is the equivalent of what happens when someone types a regular key to catch the problem where the tab is blurring things before the keyUp is done. Because of this last part, it may not be the perfect solution for everyone, but it was the only thing that could solve my problem. So, here is the code that makes it work for me. I hope someone else will find it useful.
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//Removed to prevent onBlur/Tab causing invalid selections
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
},
onPaste: function(){
var me = this;
if (!me.readOnly && !me.disabled && me.editable) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
},
// store the last key and doQuery if relevant
onKeyUp: function(e, t) {
var me = this,
key = e.getKey();
if (!me.readOnly && !me.disabled && me.editable) {
me.lastKey = key;
// we put this in a task so that we can cancel it if a user is
// in and out before the queryDelay elapses
// perform query w/ any normal key or backspace or delete
if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
}
if (me.enableKeyEvents) {
me.callParent(arguments);
}
},
// private
assertValue: function() {
var me = this,
value = me.getRawValue(),
rec, currentValue;
if (me.forceSelection) {
if (me.multiSelect) {
// For multiselect, check that the current displayed value matches the current
// selection, if it does not then revert to the most recent selection.
if (value !== me.getDisplayValue()) {
me.setValue(me.lastSelection);
}
} else {
// For single-select, match the displayed value to a record and select it,
// if it does not match a record then revert to the most recent selection.
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//Try and query the value to find it as a "catch" for the blur happening before the last keyed value was entered
me.doRawQuery();
//Get the new value to use
value = me.getRawValue();
//Copy of the above/same assert value check
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//This is the original "else" condition
me.setValue(me.lastSelection);
}
}
}
}
me.collapse();
},
doTypeAhead: function() {
var me = this;
if (!me.typeAheadTask) {
me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me);
}
if (me.lastKey != Ext.EventObject.BACKSPACE && me.lastKey != Ext.EventObject.DELETE) {
//Changed to not use the delayed task if 0 or less
if (me.typeAheadDelay > 0) {
me.typeAheadTask.delay(me.typeAheadDelay);
} else {
me.onTypeAhead();
}
}
}
});

how to validate data on drop event of itemselecter in extjs?

we not like to add three word again in selected item.
we like to validate on drag and drop event and need to show message that u already added this record.
i try with below code but not able to fine relevant event for validate
listeners: {
added:function(obj,event){
console.log("added");
},change:function(obj,event){
console.log("change");
},removed:function(obj,event){
console.log("removed");
}, blur:function(obj,event){
console.log("blur");
}, click: function( obj) {
console.log('click');
}, select: function( obj) {
console.log('select');
}
}
please see attached image bellow.
I am Using Extjs 3.4
The change event fires when an item is selected or deselected... But at this stage, you won't be able to prevent it anymore. So, apparently, your best move is to override the onAddBtnClick method:
{
xtype: 'itemselector'
// ... config
,onAddBtnClick: function() {
var me = this,
selected = me.getSelections(me.fromField.boundList),
i, l, record;
var toStore = this.toField.boundList.getStore(),
idField = 'value', // or 'id', or whatever you want
selectedIds = Ext.pluck(Ext.pluck(toStore.getRange(), 'data'), idField),
accepted = [], rejected = [];
for (i=0, l=selected.length; i<l; i++) {
record = selected[i];
if (selectedIds.indexOf(record.get(idField)) === -1) {
accepted.push(record);
} else {
rejected.push(record);
}
}
if (rejected.length) {
// warning msg
}
me.moveRec(true, accepted);
me.toField.boundList.getSelectionModel().select(accepted);
}
}

Is it possible to reinitialize a CKEditor Combobox/Drop Down Menu?

How do I dynamically update the items in a drop down?
I have a custom plugin for CKEditor that populates a drop down menu with a list of items which I can inject into my textarea.
This list of items comes from a Javascript array called maptags, which is updated dynamically for each page.
var maptags = []
This list of tags gets added to the drop down when you first click on it by the init: function. My problem is what if the items in that array change as the client changes things on the page, how can I reload that list to the updated array?
Here is my CKEditor Plugin code:
CKEDITOR.plugins.add('mapitems', {
requires: ['richcombo'], //, 'styles' ],
init: function (editor) {
var config = editor.config,
lang = editor.lang.format;
editor.ui.addRichCombo('mapitems',
{
label: "Map Items",
title: "Map Items",
voiceLabel: "Map Items",
className: 'cke_format',
multiSelect: false,
panel:
{
css: [config.contentsCss, CKEDITOR.getUrl(editor.skinPath + 'editor.css')],
voiceLabel: lang.panelVoiceLabel
},
init: function () {
this.startGroup("Map Items");
//this.add('value', 'drop_text', 'drop_label');
for (var this_tag in maptags) {
this.add(maptags[this_tag][0], maptags[this_tag][1], maptags[this_tag][2]);
}
},
onClick: function (value) {
editor.focus();
editor.fire('saveSnapshot');
editor.insertHtml(value);
editor.fire('saveSnapshot');
}
});
}
});
I think I just solved this actually.
Change your init like this:
init: function () {
var rebuildList = CKEDITOR.tools.bind(buildList, this);
rebuildList();
$(editor).bind('rebuildList', rebuildList);
},
And define the buildList function outside that scope.
var buildListHasRunOnce = 0;
var buildList = function () {
if (buildListHasRunOnce) {
// Remove the old unordered list from the dom.
// This is just to cleanup the old list within the iframe
$(this._.panel._.iframe.$).contents().find("ul").remove();
// reset list
this._.items = {};
this._.list._.items = {};
}
for (var i in yourListOfItems) {
var item = yourListOfItems[i];
// do your add calls
this.add(item.id, 'something here as html', item.text);
}
if (buildListHasRunOnce) {
// Force CKEditor to commit the html it generates through this.add
this._.committed = 0; // We have to set to false in order to trigger a complete commit()
this.commit();
}
buildListHasRunOnce = 1;
};
The clever thing about the CKEDITOR.tools.bind function is that we supply "this" when we bind it, so whenever the rebuildList is triggered, this refer to the richcombo object itself which I was not able to get any other way.
Hope this helps, it works fine for me!
ElChe
I could not find any helpful documenatation around richcombo, i took a look to the source code and got an idea of the events i needed.
#El Che solution helped me to get through this issue but i had another approach to the problem because i had a more complex combobox structure (search,groups)
var _this = this;
populateCombo.call(_this, data);
function populateCombo(data) {
/* I have a search workaround added here */
this.startGroup('Default'); /* create default group */
/* add items with your logic */
for (var i = 0; i < data.length; i++) {
var dataitem = data[i];
this.add(dataitem.name, dataitem.description, dataitem.name);
}
/* other groups .... */
}
var buildListHasRunOnce = 0;
/* triggered when combo is shown */
editor.on("panelShow", function(){
if (buildListHasRunOnce) {
// reset list
populateCombo.call(_this, data);
}
buildListHasRunOnce = 1;
});
/* triggered when combo is hidden */
editor.on("panelHide", function(){
$(_this._.list.element.$).empty();
_this._.items = {};
_this._.list._.items = {};
});
NOTE
All above code is inside addRichCombo init callback
I remove combobox content on "panelHide" event
I repopulate combobox on "panelShow" event
Hope this helps

Categories

Resources