mrSpear
mrSpear

Reputation: 473

AngularJS scoping issue with multiple directives

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:

  1. On mouseover, mark grid item as "activated" and add "active" class
  2. on up/down/left/right arrow keys select the next grid row - applying the "activated" status and active class to it.

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

Answers (1)

mrSpear
mrSpear

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

Related Questions