问题
My goal is to do some action when a particular directive receives the class 'focused' through ng-class. But watching for changes on each of the directives seems to be doing weird things I don't understand. Here is the code first:
http://plnkr.co/edit/6Te8O2mlBI058DpwGF7f?p=preview
index.html
<test-question
ng-class="{'focused': tracer.focus == 1}"
focusnum = "{{n}}"
ng-click="tracer.focus = 1"
>
Test Question 1
</test-question>
<test-question
ng-class="{'focused': tracer.focus == 2}"
focusnum = "{{n}}"
ng-click="tracer.focus = 2"
>
Test Question 2
</test-question>
<test-question
ng-class="{'focused': tracer.focus == 3}"
focusnum = "{{n}}"
ng-click="tracer.focus = 3"
>
Test Question 3
</test-question>
script.js
angular.module('sampleApp', [])
.controller("SampleController", function($scope) {
$scope.tracer = {
focus: 1
}
})
.directive('testQuestion', function() {
return {
restrict: 'E',
link: function($scope, el, attrs) {
$scope.$watch(function() {
return el.attr('class');
}, function(newValue, oldValue) {
// do something here when directive gets class focused
console.log("text", el.text());
console.log("newValue", newValue);
console.log("oldValue", oldValue);
});
}
}
});
Initially the first test question is highlighted. When you click the second question I expect the console to log
"text" Test Question 1
"newValue" undefined
"oldValue" focused
"text" Test Question 2
"newValue" focused
"oldValue" undefined
However the console actually logs
"text" Test Question 1
"newValue" focused
"oldValue" undefined
As a result, of this weird behavior I cannot accurately detect when a directive receives the class focused. It appears as if the watch is one cycle late. It would be great if a fellow friend could help me understand why this is happening. Thank you!
Additional
After I click question 2, and if I subsequently click question 3 the console logs
"text" Test Question 1
"newValue"
"oldValue" focused
"text" Test Question 2
"newValue" focused
"oldValue" undefined
回答1:
Yes is become late, there is no proper solution for this condition yet. However you can do something below.
$scope.$watch(function() {
return el.hasClass('focused');
}, function(newValue, oldValue) {
// now you should use javascript setTimeout for check after $diggest
var myOldValue = newValue; //this mean newValue realy oldValue yet.
setTimeout(function(){
var myNewValue= el.hasClass('focused');
console.log(myNewValue, myOldValue)
//do staff here
},0)
//note that, don't use angular $timeout it may couse recursive stack
});
WHY?
Because you are watching an async event which outside of AngularJS. Namely need to called at the end, to tell AngularJS that an asynchronous event just occurred by using $scope.$apply().
This like our custom $('button').on('click', ...) events which need to $scope.$apply(). However you may ask that, I am watching changes done by using ng-class. This is rigth, but your watcher function is called before ng-class done its work. Namely you aren't watching "ng-class" you are watching class attribute. For this reason the changes you are listen will noticed to angular in the next diggest.
If you tried to access current class in your watcher, you will get old value, because ng-class hasn't done its work yet.
If you tried to access current class javascript setTimeout function in your in your watcher, you will get the correct vallue, because setTimeout would be called after all angular works done.
This case is valid for all changes outside of AngularJS. When you tried to watch an element heigth, width, color, an object property etc. You can use it in quietness.
NOTE Think watching changes outside of AngularJS like a promise and setTimeout would be your callback.
来源:https://stackoverflow.com/questions/27786814/watch-to-monitor-change-in-class-on-directive-firing-1-cycle-late