I'm trying to figure out a way to have an Excel-like behavior with the Grids on Ext JS.
Here is the sample grid I am working with. So far we can already naviguate through the cells with the arrows but only in edit mode.
However what I am trying to reach is the naviguation with the arrows, TAB and Enter keys outside of the edit mode, just like excel.
I tried to integrate this piece of code which overrides the Editor class, hoping that it would change the behavior of the cells but it doesn't change a thing.
I believe this is the most important part that overrides the Editor class and tries to include the keys input :
Ext.override(Ext.Editor, {
startEdit: function (el, value) {
var me = this,
field = me.field;
me.completeEdit();
me.boundEl = Ext.get(el);
value = Ext.isDefined(value) ? value : me.boundEl.dom.innerHTML;
if (!me.rendered) {
me.render(me.parentEl || document.body);
}
if (me.fireEvent('beforestartedit', me, me.boundEl, value) !== false) {
me.startValue = value;
me.show();
field.reset();
if (deleteGridCellValue) {
field.setValue('');
me.editing = true;
me.completeEdit();
deleteGridCellValue = false; // reset global variable
}
else {
if (newGridCellValue == '') {
// default behaviour of Ext.Editor (see source if needed)
field.setValue(value);
}
else {
// custom behaviour to handle an alphanumeric key press from non-edit mode
field.setRawValue(newGridCellValue);
newGridCellValue = ''; // reset global variable
if (field instanceof Ext.form.field.ComboBox) {
// force the combo box's filtered dropdown list to be displayed (some browsers need this)
field.doQueryTask.delay(field.queryDelay);
}
}
me.realign(true);
field.focus(false, 10);
if (field.autoSize) {
field.autoSize();
}
me.editing = true;
}
}
}
});
This is the first time that I am working on a project that is outside of Comp-Sci classes so any help would be very much appreciated. Thanks !
Related
I'm wondering if the behavior I'm seeing is a bug and figured I'd ask here before submitting to github.
I need to be able to create, save, and edit saved email templates in the Quill editor. I also need to be able to use the output from Quill in an email, formatted how it was entered in the WYSIWYG editor, so I'm saving the HTML output with inline styles.
I have an issue with the indentation not rendering as it was entered when I load in a saved email template. I'm implementing the IndentAttributor like the following comment:
https://github.com/quilljs/quill/issues/1274#issuecomment-303619625
I dug into what Quill is doing and found in matchStyles() a tab character is getting inserted if textIndent is found.
Offending code in quill.js:
function matchStyles(node, delta) {
var formats = {};
var style = node.style || {};
if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {
formats.italic = true;
}
if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) {
formats.bold = true;
}
if (Object.keys(formats).length > 0) {
delta = applyFormat(delta, formats);
}
if (parseFloat(style.textIndent || 0) > 0) {
// Could be 0.5in
delta = new _quillDelta2.default().insert('\t').concat(delta);
}
return delta;
}
I tested a change to the quill.js node_module to check for text-indent style. However, I'm not trying to hack the editor for a specific need and I'm not familiar enough with the editor to know whether or not this will have a negative impact on anything else.
function matchStyles(node, delta) {
console.log('in matchStyles()');
var formats = {};
var style = node.style || {};
if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {
formats.italic = true;
}
if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) {
formats.bold = true;
}
if (Object.keys(formats).length > 0) {
delta = applyFormat(delta, formats);
}
if ((parseFloat(style.textIndent || 0) > 0) && !style.cssText.includes('text-indent')) { //fix to check for inline style
// Could be 0.5in
delta = new _quillDelta2.default().insert('\t').concat(delta); //problem here...
}
return delta;
}
Example project showing the issue:
https://stackblitz.com/edit/github-acpnmm
Highlight or inspect the initial text (coming from app.reducer.ts initial state) and you will see the tab character that was inserted.
Should Quill be recognizing the IndentAttributor and not insert the tab character?
I see you are using Quill in Angular project. I would suggest you to check this package: https://www.npmjs.com/package/ngx-quill. With this package you can choose the type of data (Text, HTML or Delta) that the Quill will process, and you can bind the Quill content to some variable. That way you can just store that value of that variable in the database. When you need to load the content again, fetch that value from the database, and again bind the Quill content to that value.
we are using extjs 4.1 and i have run into a weird issue. We have a group of 3 tabs and those tabs contains extjs grids. extjs grids in the first 2 are loading the data fine and also have filter option. extjs grid in the 3rd tab, loads the data but does not show the filter menu. However, when the page is refreshed while on the 3rd tab, the grid shows the filter menu. i am new to extjs. please help.
ext-all-debug-w-comments-v4.1.2a.js:144232 Uncaught TypeError: Cannot read property 'enable' of null
at constructor.showMenuBy (ext-all-debug-w-comments-v4.1.2a.js:144232)
at constructor.onHeaderTriggerClick (ext-all-debug-w-comments-v4.1.2a.js:141028)
at constructor.onElClick (ext-all-debug-w-comments-v4.1.2a.js:142124)
at HTMLDivElement.eval (eval at cacheableFunctionFactory (ext-all-debug-w-comments-v4.1.2a.js:683), <anonymous>:6:13)
at HTMLDivElement.wrap (ext-all-debug-w-comments-v4.1.2a.js:15197)
the following line is where the error is happening but this line is within extjs
showMenuBy: function(t, header) {
var menu = this.getMenu(),
groupMenuItem = menu.down('#groupMenuItem'), //this menu is null and therefore the error but this is auto generated by extjs
groupMenuMeth = header.groupable === false ? 'disable' : 'enable',
groupToggleMenuItem = menu.down('#groupToggleMenuItem');
groupMenuItem[groupMenuMeth](); //this statement is null and throws the error
if (groupToggleMenuItem) {
groupToggleMenuItem[this.view.store.isGrouped() ? 'enable' : 'disable']();
}
Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
}
//code from the onHeaderTrigerClick stackTrace:
onHeaderTriggerClick: function(header, e, t) {
// generate and cache menu, provide ability to cancel/etc
var me = this;
if (header.fireEvent('headertriggerclick', me, header, e, t) !== false && me.fireEvent("headertriggerclick", me, header, e, t) !== false) {
me.showMenuBy(t, header);
}
}
//code from onElClick:
onElClick: function(e, t) {
// The grid's docked HeaderContainer.
var me = this,
ownerHeaderCt = me.getOwnerHeaderCt();
if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
// Firefox doesn't check the current target in a within check.
// Therefore we check the target directly and then within (ancestors)
if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) {
ownerHeaderCt.onHeaderTriggerClick(me, e, t);
// if its not on the left hand edge, sort
} else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) {
me.toggleSortState();
ownerHeaderCt.onHeaderClick(me, e, t);
}
}
}
in every case, parameter header is null.
see Ext.grid.feature.Grouping.
It seems probably a css issue, might be caused by the order in which components load and height applied to header.
Use below css:
.ui-grid-header-cell{
height:60px;
max-height:60px;
}
Identify the id of header component and use it in place of .ui-grid-header-cell
I have a grid and I want to disable appearance of loading mask on it (to prevent double loading mask because I add loading mask to its parent component) at the the execution of certain scripts.
I've tried something like this
var myGridView = myGrid.getView();
myGridView.loadMask = false;
// I want so at this data loding did not appear loading mask
myGrid.getStore().load();
myGridView.loadMask = true;
but it doesnt work.
Any suggestions?
You can use setDisabled() method for LoadMask instance:
var myGridView = myGrid.getView();
myGridView.loadMask.setDisabled(true);
myGrid.getStore().load(function () {
myGridView.loadMask.setDisabled(false);
});
As well you can use enable(), disable() methods.
After carefully reading source code of grid, load mask and store I can suggest this small override
Ext.override(Ext.grid.Panel, {
setLoading: function (load) {
var me = this,
view = me.getView(),
store = me.getStore();
me.callParent(arguments);
if (Ext.isBoolean(load) && view && view.loadMask && view.loadMask.isLoadMask && store) {
if (load) {
view.loadMask.bindStore && view.loadMask.bindStore(store);
} else {
view.loadMask.unbindStoreListeners(store);
}
}
}
});
This sounds crazy, but spinner knows about grid's store. And even has (protected) methods to work with http://docs.sencha.com/extjs/5.1.1/Ext.LoadMask.html#method-unbindStoreListeners
http://docs.sencha.com/extjs/4.1.3/#!/api/Ext.LoadMask-method-unbindStoreListeners
I am using ui-grid - v3.0.0-rc.22 - 2015-06-15.
It is configured to use external sorting, which works fine.
Now i have the requirement to change the sorted column from outside with a select box. On every change of the select box it fires external sorting and the data in the grid is updated correctly. It also updates the gridOptions.columnDefs: It sets the sort object of all columns except the correct one to undefined and updates the sorted column.
But there is one problem, the current sorted column indicator (in the column header) is not updated as it should be.
I tried using gridApi.core.notifyDataChange() with "options" or"column" as parameter value but it didn't work also.
How to update the sort-indicators in ui-grid programmatically?
Here is a part of the code triggered by the select box:
function updateSortColumn() {
if ($rootScope.QuickSearch.sortBy !== undefined) {
$scope.gridOptions.columnDefs.forEach(function (col) {
if (col.field === $rootScope.QuickSearch.sortBy) {
col.sort = {
direction: $rootScope.QuickSearch.sortOrder,
priority: 0
};
}
else
{
col.sort = undefined;
}
});
}
if($scope.gridApi !== undefined)
{
$scope.gridApi.core.notifyDataChange( uiGridConstants.dataChange.OPTIONS );
$scope.gridApi.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
}
}
You could use the function "sortColumn" of the ui-grid, like this:
$scope.gridApi.grid.sortColumn(column, directionOrAdd, add)
here is the source code of this function : ui-grid source code
in your example it will give somthing like this :
function updateSortColumn() {
if ($rootScope.QuickSearch.sortBy !== undefined) {
$scope.gridOptions.columnDefs.forEach(function (col) {
if (col.field === $rootScope.QuickSearch.sortBy) {
$scope.gridApi.grid.sortColumn(col,$rootScope.QuickSearch.sortOrder);
}
});
}
}
$rootScope.QuickSearch.sortOrder must be in (uiGridConstants.ASC|uiGridConstants.DESC). You do not have to provide it.
I had the same problem -- the solution in my case was what Gho5t helpfully mentioned in a comment on another answer on this question.
I'm adding this response so the solution can have more visibility (alongside a more complete code example).
I needed a way to hook into the sort event on a grid and sort other grids on the page by the same column (they all have the same column definitions).
I was incorrectly passing the gridOptions.colDefinition object to the sortColumn() method and the column header sort indicator was not updating.
The grid.column object was what the sortColumn() method was looking for and caused things to work as expected.
// sortColumns is an array of column objects that gets passed in when a grid column is sorted (this code only considers the first sorted column)
// secondGridObj is an object defined elsewhere that has a reference to another grid's gridApi object
gridApi.core.on.sortChanged(null, function (grid, sortColumns) {
if (sortColumns.length) {
var sortDirection = (sortColumns[0].sort) ? sortColumns[0].sort.direction || uiGridConstants.ASC : uiGridConstants.ASC;
var matchingColumn = _.find(secondGridObj.gridApi.grid.columns, function (v2) { return v2.field === sortColumns[0].field; });
if (matchingColumn) {
secondGridObj.gridApi.grid.sortColumn(matchingColumn, sortDirection, false)
.then(function() {
secondGridObj.gridApi.grid.notifyDataChange(uiGridConstants.dataChange.COLUMN);
});
}
}
});
I been trying to do checkbox Checkall and UnCheckall using subscribe and i'm partially successful doing that but i am unable to find a fix in couple of scenarios when i am dealing with subscribe .
Using subscribe :
I am here able to checkAll uncheckAll but when i uncheck a child checkbox i.e test1 or test2 i need my parent checkbox name also to be unchecked and in next turn if i check test1 the parent checkbox should be checked i.e keeping condition both child checkboxes are checked .
For fiddle : Click Here
ViewModel :
self.selectedAllBox.subscribe(function (newValue) {
if (newValue == true) {
ko.utils.arrayForEach(self.People(), function (item) {
item.sel(true);
});
} else {
ko.utils.arrayForEach(self.People(), function (item) {
item.sel(false);
});
}
});
The same scenario can be done perfectly in easy way using computed but due some performance issues i need to use subscribe which is best way it wont fire like computed onload .
Reference : Using computed same thing is done perfectly check this Fiddle
I tried to use change event in individual checkbox binding but its a dead end till now.
Any help is appreciated .
Your subscription only applies to edits on the selectedAllBox. To do what you want, you'll need subscriptions on every Person checkbox as well, to check for the right conditions and uncheck the selectedAllBox in the right situations there.
It strikes me as odd that this would be acceptable but using computed() is not. Maybe you should reconsider that part of your answer. I would much rather compute a "isAllSelected" value based on my viewModel state, then bind the selectedAllBox to that.
I solved a similar problem in my own application a couple of years ago using manual subscriptions. Although the computed observable method is concise and easy to understand, it suffers from poor performance when there's a large number of items. Hopefully the code below speaks for itself:
function unsetCount(array, propName) {
// When an item is added to the array, set up a manual subscription
function addItem(item) {
var previousValue = !!item[propName]();
item[propName]._unsetSubscription = item[propName].subscribe(function (latestValue) {
latestValue = !!latestValue;
if (latestValue !== previousValue) {
previousValue = latestValue;
unsetCount(unsetCount() + (latestValue ? -1 : 1));
}
});
return previousValue;
}
// When an item is removed from the array, dispose the subscription
function removeItem(item) {
item[propName]._unsetSubscription.dispose();
return !!item[propName]();
}
// Initialize
var tempUnsetCount = 0;
ko.utils.arrayForEach(array(), function (item) {
if (!addItem(item)) {
tempUnsetCount++;
}
});
var unsetCount = ko.observable(tempUnsetCount);
// Subscribe to array changes
array.subscribe(function (changes) {
var tempUnsetCount = unsetCount();
ko.utils.arrayForEach(changes, function (change) {
if (change.moved === undefined) {
if (change.status === 'added') {
if (!addItem(change.value))
tempUnsetCount++;
} else {
if (!removeItem(change.value))
tempUnsetCount--;
}
}
});
unsetCount(tempUnsetCount);
}, null, 'arrayChange');
return unsetCount;
}
You'll still use a computed observable in your viewmodel for the the select-all value, but now it'll only need to check the unselected count:
self.unselectedPeopleCount = unsetCount(self.People, 'Selected');
self.SelectAll = ko.pureComputed({
read: function() {
return self.People().length && self.unselectedPeopleCount() === 0;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(value);
});
}
}).extend({rateLimit:0});
Example: http://jsfiddle.net/mbest/dwnv81j0/
The computed approach is the right way to do this. You can improve some performance issues by using pureComputed and by using rateLimit. Both require more recent versions of Knockout than the 2.2.1 used in your example (3.2 and 3.1, respectively).
self.SelectAll = ko.pureComputed({
read: function() {
var item = ko.utils.arrayFirst(self.People(), function(item) {
return !item.Selected();
});
return item == null;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(value);
});
}
}).extend({rateLimit:1});
http://jsfiddle.net/mbest/AneL9/98/