Scott
Scott

Reputation: 6736

AngularJS - directive scope not setting ng-class on element

Plunker

I have a table of data:

<table class="table table-hover" ng-class="dndElemClass" drag-and-drop>
  ...
</table>

My goal is to give the table a drop shadow by assigning $scope.dndElemClass = 'on-drag-enter' on the element's ondragenter event listener:

.on-drag-enter {
    -webkit-box-shadow: -3px 3px 5px 3px #ccc;
    ...
}

The onDragEnter directive is as follows:

directive('dragAndDrop', function() {
    return {
        restrict: 'A',
        controller: function($scope) {
            $scope.dndElemClass = '';
        },
        link: function($scope, elem, attr) {
            $scope.$watch('currentFolder.canUpload', function(newValue, oldValue) {
                if (newValue && Modernizr.draganddrop && window.File) {
                    elem[0].ondragenter = function(evt) {
                        evt.stopPropagation();
                        evt.preventDefault();
                        $scope.$apply(function() {
                            $scope.dndElemClass = 'on-drag-enter';
                        });
                    };
                    elem[0].ondragleave = function(evt) {
                        evt.stopPropagation();
                        evt.preventDefault();
                        $scope.$apply(function() {
                            $scope.dndElemClass = '';
                        });
                    };
                    elem[0].ondrop = function(evt) {
                        ...
                    };
                }
            });
        }
    }
})

Despite assigning the value of $scope.dndElemClass in the ondragenter and ondragleave event listeners, the <table> doesn't appear to be recognizing the value and assigning the class as no dropshadow appears.

Thus far I've tested that it does recognize the value if I set the class in the controller property of the directive where I have it assigned to blank in the above code, so I know it will accept it from the directive. With the class set in the controller as a test, if I trigger the ondragenter listener, it removes the class. I've also confirmed that the $scope.$apply() is properly assigning the value of scope.dndElemClass with logging, but for whatever reason, when set in the event listeners's $scope.$apply(), the table's ng-class attribute won't recognize the variable assignment and thinks it's empty.


UPDATE: As per Josh's comment, I cleaned up the code so that I didn't have to $apply the variable assignment in the event listener callbacks.

directive('dragAndDrop', function() {
    return {
        restrict: 'A',
        controller: function($scope) {
            $scope.dndElemClass = '';
        },
        link: function($scope, elem, attr) {
            $scope.$watch('currentFolder.canUpload', function(newValue, oldValue) {
                if (newValue && Modernizr.draganddrop && window.File) {
                    elem.bind('dragenter', function(evt) {
                        evt.stopPropagation();
                        evt.preventDefault();
                        $scope.dndElemClass = 'on-drag-enter';
                    });
                    elem.bind('dragleave', function(evt) {
                        evt.stopPropagation();
                        evt.preventDefault();
                        $scope.dndElemClass = '';
                    });
                    elem.bind('drop', function(evt) {
                        //...
                    });
                }
            });
        }
    }
})

Still no luck. I can verify it is executing the callbacks with logging, but no luck on getting the variable assignment to be recognized by the table's ng-class attribute.


UPDATE 2: I am even more confused after reading through AngularJS's documentation on ngClass. To me, I thought it was as simple as setting the name(s in an array) of the classes you want to a variable in the current controller's (or in my case, the directive's) $scope, then specify that variable's name like you would anywhere else in the element's ng-class="" attribute. But as I'm reading, it seems like it's more much complicated as people are using expressions or toggling the class name(s).

Using the idea of toggling, I forked my plunker to recreate the situation setting $scope.dndElemClass to a boolean value based on whether the user triggers dragenter or dragleave. I also included $scope.$apply() for good measure, as I am finding that I don't understand the advantage of angular.bind() over .addEventListener or .ondragenter = function() {};. Regardless, none of this has caused the table's class to get set as I would expect it to.

Upvotes: 1

Views: 4612

Answers (2)

Tushar Pandya
Tushar Pandya

Reputation: 128

try to set class in scope then use class={{classVariable}} !!! This works for me.

Upvotes: 0

James Lim
James Lim

Reputation: 13062

I am not sure if you still need an answer, but I might have fixed your plunk.

Inside the CurrentFolder controller, add

$scope.dndElemClass  = '';

Then wrap $scope.dndElemClass inside $apply.

$scope.$apply(function() { $scope.dndElemClass = 'on-drag-enter'; });

Check the complete plunk for a clearer picture.

enter image description here

Upvotes: 1

Related Questions