Open alexandery opened 9 years ago
The cellNav feature is what enables navigation using the keyboard.
@PaulL1
Thanks for you reply. However the cellNav feature is not what would enable me to navigate rows - it forces me to navigate cells (which confuses users :( - this is the biggest issue with this approach ) plus breaks my code by not reporting when a row selection has changed. Plus, in some cases I need to be able to select multiple rows which makes this more complicated.
Sure, I can use this workaround and adjust all grids to work with cellNav cell change event, but I believe that another feature (rowNav?) should be a part of the solution and take care of this functionality at a row level. What do you think?
That would be a potential addition, although there is an element of feature bloat that we get if we keep adding features for every little thing. The two options would be:
Probably the choice between the two would depend a little on how much shared logic there was.
Can we have this issue marked as enhancement / feature? Focusing and navigating the whole row is very helpful as allows the ui-grid to be used as an enhanced substitute of the <select>
tag.
I've played with cellnav.js
and it seems to me that it needs to be changed only in one place (the logic being the same). In the CELL_NAV_EVENT
instead of focusing when the cell matches, do focus when the row matches. Something like this:
// rowNav = enables navigating by rows instead by cell
if ((rowNav && rowCol.row === $scope.row) ||
(!rowNav && rowCol.row === $scope.row && rowCol.col === $scope.col)) {
setFocused();
if(rowCol.col === $scope.col) {
// This cellNav event came from a keydown event so we can safely refocus
if (rowCol.hasOwnProperty('eventType') && rowCol.eventType === uiGridCellNavConstants.EVENT_TYPE.KEYDOWN) {
$elm.find('div')[0].focus();
}
}
}
I'm happy to reopen, and also happy to accept a PR with this functionality.
@nippur72 Where did you define rowNav?
@longshot5112 for my test I hardcoded it manually e.g.var rowNav = true;
Has anyone had any luck implementing a solution for this yet. @nippur72 I've tried you suggestion and it seems to mostly work, but when you get to the bottom row the scrolling does not align with the row that is selected. Especially if you are holding down the down arrow key.
@smithscripts Sorry, I sort of dropped this. Seemed like ui-grid guys were not interested in building this out and I simply didn't have time to invest into building it out myself. I'd love to have it, but it's a nice-to-have for me at this moment.
I would like to see this. UI Grid is perfect for me but I need to have elegant way to map rows to underlining models. In my case at least row navigation would be better because row in grid equal to model instance.
I will try maybe to implement this and do PR but I am struggling at Uni and my day-job is pushing for deadlines.
Hi,
You can achieve this by doing below:
$scope.gridApi.cellNav.on.navigate($scope,function(newRowCol, oldRowCol){
$scope.gridApi.selection.selectRow(newRowCol.row.entity);
})
Thank you @gokhanoner! Works perfectly!
If you're using the cellNav, you won't be able to select cells text. If you want key up/down navigation without the cellNav, I wrote a directive based on the cellNav functionality:
function uiGridKeyNav($compile, gridUtil) {
return {
require: '^uiGrid',
scope: false,
link: function ($scope, $elm, $attrs, uiGridCtrl) {
var grid = uiGridCtrl.grid;
var focuser = $compile('<div class="ui-grid-focuser" role="region" aria-live="assertive" aria-atomic="false" tabindex="0" aria-controls="' + grid.id + '-aria-speakable ' + grid.id + '-grid-container' + '" aria-owns="' + grid.id + '-grid-container' + '"></div>')($scope);
$elm.append(focuser);
$elm.bind('click', function () {
gridUtil.focus.byElement(focuser[0]);
});
focuser.bind('keydown', function (e) {
$scope.$apply(function () {
var selectedEntities,
visibleRows,
selectedIndex;
if (e.keyCode !== 38 && e.keyCode !== 40)
return;
selectedEntities = grid.api.selection.getSelectedRows();
if (selectedEntities.length === 1) {
visibleRows = grid.getVisibleRows();
selectedIndex = visibleRows.map(function (item) {
return item.entity;
}).indexOf(selectedEntities[0]);
if (selectedIndex < 0)
return;
if (e.keyCode === 38 && selectedIndex > 0)
--selectedIndex;
if (e.keyCode === 40 && selectedIndex < grid.options.data.length - 1)
++selectedIndex;
grid.api.selection.selectRowByVisibleIndex(selectedIndex);
grid.api.core.scrollToIfNecessary(visibleRows[selectedIndex], grid.columns[0]);
}
});
});
}
};
}
var commonDirectivesModule = angular.module('common-directives');
commonDirectivesModule.directive('uiGridKeyNav', ['$compile', 'gridUtil', uiGridKeyNav]);`
Using the directive on the grid element:
<div ui-grid="vm.options" ui-grid-selection ui-grid-key-nav>
The best solution that worked for me was updating the original ui-grid.js code and replacing 1 line of code in the following section:
$scope.$on(uiGridCellNavConstants.CELL_NAV_EVENT, function (evt, rowCol, modifierDown) {
var isFocused = grid.cellNav.focusedCells.some(function(focusedRowCol, index){
return (focusedRowCol.row === $scope.row && focusedRowCol.col === $scope.col);
});
if (isFocused){
setFocused();
} else {
clearFocus();
}
});
You'll need to replace:
return (focusedRowCol.row === $scope.row && focusedRowCol.col === $scope.col);
with:
return (focusedRowCol.row === $scope.row);
That's it. The rest is done by the original code.
I used the directive provided by @lotri1 (Thanks!). I made some modifications:
Here's the code:
myAppModule.directive('uiGridKeyNav', ['$compile', 'gridUtil', function($compile, gridUtil) {
return {
require: '^uiGrid',
scope: false,
link: function ($scope, $elm, $attrs, uiGridCtrl) {
var grid = uiGridCtrl.grid;
var focuser = $compile('<div class="ui-grid-focuser" role="region" aria-live="assertive" aria-atomic="false" tabindex="0" aria-controls="' + grid.id + '-aria-speakable ' + grid.id + '-grid-container' + '" aria-owns="' + grid.id + '-grid-container' + '"></div>')($scope);
$elm.append(focuser);
$elm.bind('click', function () {
gridUtil.focus.byElement(focuser[0]);
});
focuser.bind('keydown', function (e) {
$scope.$apply(function () {
var selectedEntities,
visibleRows,
selectedIndex;
if (e.keyCode !== 38 && e.keyCode !== 40 && e.keyCode !== 37 && e.keyCode !== 39
&& e.keyCode !== 33 && e.keyCode !== 34 && e.keyCode !== 13)
return;
selectedEntities = grid.api.selection.getSelectedRows();
if (selectedEntities.length === 1) {
visibleRows = grid.getVisibleRows();
selectedIndex = visibleRows.map(function (item) {
return item.entity;
}).indexOf(selectedEntities[0]);
if (selectedIndex < 0)
return;
// Enter
if (e.keyCode === 13) {
// You might want to do something here like:
//grid.api.selection.unSelectRow(selectedEntities[0], e);
// This way you can pass the enter key to your gridApi.selection.on.rowSelectionChanged event handler when the the row is reselected again below
// Left
} else if (e.keyCode === 37) {
grid.api.treeBase.collapseRow(grid.rowHashMap.get(selectedEntities[0]));
// Right
} else if (e.keyCode === 39) {
grid.api.treeBase.expandRow(grid.rowHashMap.get(selectedEntities[0]));
// Up
} else if (e.keyCode === 38 && selectedIndex > 0) {
--selectedIndex;
// Down
} else if (e.keyCode === 40 && selectedIndex < grid.getVisibleRows().length - 1) {
++selectedIndex;
// pgUp
} else if (e.keyCode === 33 && selectedIndex > 0) {
selectedIndex = selectedIndex - 15;
if (selectedIndex < 0) {
selectedIndex = 0;
}
// pgDown
} else if (e.keyCode === 34 && selectedIndex < grid.getVisibleRows().length - 1) {
selectedIndex = selectedIndex + 15;
if (selectedIndex > grid.getVisibleRows().length - 1) {
selectedIndex = grid.getVisibleRows().length - 1;
}
}
grid.api.selection.selectRowByVisibleIndex(selectedIndex, e);
grid.api.core.scrollToIfNecessary(visibleRows[selectedIndex], grid.columns[0]);
}
});
});
}
};
}]);
Thanks for the key directive!
Just in case its of any use to anyone, changing $elm.bind like this will allow using the column filters:
var clickTarget = getEventTarget(e);
if (clickTarget.tagName !== 'INPUT') {
gridUtil.focus.byElement(focuser[0]);
}
... where getEventTarget is:
function getEventTarget(e) {
e = e || window.event;
return e.target || e.srcElement;
}
@lentyaishe Great solution, it works fine for me.
Thanks
@lentyaishe I really like your directive but it doesn't work for me with the down arrow key, it moves down once but not again. Am I missing something? Also would there be a way to introduce a 'ctrl'/'shift' and arrow option?
Thanks
@alib192 Unfortunately I'm quite far now from the project in which I used my workaround. Try to check that you've followed the instructions correctly and didn't miss something since it worked for @ciceroaferreira. Or just try to debug and check why it works only once.
Any update on this issue? I believe this bug still exists in the latest version of angular ui grid, which is 4.0.7. Issue: Unable to use cell navigation and drag-and-drop at the same time!!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically closed because it has not had recent activity. If you believe that this is still an issue in the latest version, feel free to re-open it. Thank you for your contributions.
Note: add help wanted to this issue to prevent automatic closing
Unless I'm missing something, I don't seem to be able to navigate rows in the grid using my keyboard. Is this supported? If so, what do I need to set to enable the functionality?