Reputation: 473
I'm working on adding some keyboard navigation support for a grid implementation called trNgGrid. I have some of the code working, however I believe a scoping problem is what lies between victory and defeat.
What I'm trying to achieve:
I have the mousedown functionality implemented and it is working perfectly. On initialization I have an option called gridOptions.selectedIndex that defaults to 0. This option is defined in the main parent directive - see plnkr trNgGrid.js line 945
selectedIndex: '=?' //not sure why the ? really - just mimicking others in this grid directive
On mouseover, a function is called that sets the gridOptions.selectedIndex to the index of the row being moused-over. Each row has an ng-class applied that sets the active class if the $index==gridOptions.selectedIndex.
Code to show how table rows are output using ng-repeat & my ng-class setup - see plnkr trNgGrid.js line 710-714
templatedBodyRowElement.attr("ng-repeat", "gridDisplayItem in filteredItems");
templatedBodyRowElement.attr("ng-init", "gridItem=gridDisplayItem.$$_gridItem");
templatedBodyRowElement.attr("ng-class", "{'" + TrNgGrid.rowSelectedCssClass + "':$index==gridOptions.selectedIndex}");
templatedBodyRowElement.attr("ng-mouseover", "handleMouseOver($index, $event)");
bodyDirective (mouse handling logic) - see plnkr trNgGrid.js line 1086
scope.handleMouseOver = function(index, $event) {
controller.handleMouseOver(index, $event);
}
Controller function for handlMouseOver() - see plnkr trNgGrid.js line 461
GridController.prototype.handleMouseOver = function (index, $event) {
this.gridOptions.selectedIndex = index;
}
Now, for keydown... Inside one of the several directives that make up trNgGrid, I am injecting the $document, and adding a $document listener (I need to listen for the arrow keys any time a grid is available on the page - there will never be more than one grid on a page at once). For each keydown that matches the right key codes I call a function that updates the gridOptions.selectedIndex appropriately. Based on the ng-class attribute (active: $index==gridOptions.selectedIndex) this should theoretically apply the "active" class and css styles to the row. However, this is not happening. I am attempting to reference the gridOptions identically to how the mouseover function does, but I'm wondering if the ng-repeat and its scope behavior may be interfering somehow?
Directive that contains the $document listener - see plnkr trNgGrid line 1071
.directive(bodyDirective, [ "$document",
function ($document) {
return {
restrict: 'A',
require: '^' + tableDirective,
scope: true,
compile: function (templateElement, tAttrs) {
return {
pre: function (scope, compiledInstanceElement, tAttrs, controller) {
scope.toggleItemSelection = function (item, $event) {
controller.toggleItemSelection(scope.filteredItems, item, $event);
};
scope.doRowClickAction = function (item, $event) {
controller.doRowClickAction(scope.filteredItems, item, $event);
};
scope.handleMouseOver = function(index, $event) {
controller.handleMouseOver(index, $event);
}
scope.navigateRows = function(dir, $event) {
controller.navigateRows(dir);
}
scope.selectRow = function($event) {
controller.selectRow();
}
scope.navigatePage = function(dir, $event) {
controller.navigatePage(dir);
}
},
post: function(scope, iElem, tAttrs, controller){
// Keyboard Navigation of table
$document.on("keydown", function(event) {
var keyCode = event.which;
switch (keyCode) {
case 38: //up arrow
case 37: //left arrow
scope.navigateRows("up");
break;
case 40: //down arrow
case 39: //right arrow
scope.navigateRows("down");
break;
case 13: //enter
scope.selectRow();
break;
case 33: //page up
scope.navigatePage("up");
break;
case 34: //page down
scope.navigatePage("down");
break;
}
});
}
};
}
};
}
])
Controller function that has logic for up/down/left/right/arrow - see plnkr trNgGrid line 468
GridController.prototype.navigateRows = function (dir){
var count = this.gridOptions.items.length;
if (count > 1) {
if (dir === "up") {
if (this.gridOptions.selectedIndex > 0) {
this.gridOptions.selectedIndex -= 1;
}
} else {
if (this.gridOptions.selectedIndex < count-1) {
this.gridOptions.selectedIndex += 1;
}
}
console.log(this.gridOptions.selectedIndex);
}
}
I may be asking too much to have someone look at so much code due to how much there is, but I have created a plunk to show what I've done.
http://plnkr.co/edit/y8lXuDa7zodzW4iWG2UV?p=preview
Upvotes: 0
Views: 254
Reputation: 473
Solved it. This question clued me into my problem and what had to be done:
How do I update an angularjs page after a scope update?
scope.$apply() was needed. I went ahead and wrapped it around each of the function calls that ended up modifying the scope.
scope.$apply(function(){
scope.navigateRows("up");
});
Upvotes: 1