refactor(scope.$watch): rearrange arguments passed into watcher (newValue, oldValue, scope)

As scopes are injected into controllers now, you have the reference anyway, so having scope as first argument makes no sense…

Breaks $watcher gets arguments in different order (newValue, oldValue, scope)
This commit is contained in:
Vojta Jina 2011-11-30 12:23:58 -08:00
parent 992c790f07
commit 0196411dbe
14 changed files with 61 additions and 47 deletions

View file

@ -66,7 +66,7 @@ no connection between the controller and the view.
function same(a, b, c) { return (a==b && b==c) ? a : '';}; function same(a, b, c) { return (a==b && b==c) ? a : '';};
} }
function readUrl(scope, value) { function readUrl(value) {
if (value) { if (value) {
value = value.split('/'); value = value.split('/');
$scope.nextMove = value[1]; $scope.nextMove = value[1];

View file

@ -187,7 +187,7 @@ within the unit-tests.
// example of a test // example of a test
it('should trigger a watcher', inject(function($rootScope) { it('should trigger a watcher', inject(function($rootScope) {
var scope = $rootScope; var scope = $rootScope;
scope.$watch('name', function(scope, name){ scope.$watch('name', function(name) {
scope.greeting = 'Hello ' + name + '!'; scope.greeting = 'Hello ' + name + '!';
}); });

View file

@ -618,11 +618,11 @@ you will need to specify an extra property that has two watchers. For example:
</pre> </pre>
<pre> <pre>
// js - controller // js - controller
this.$watch('locationPath', function(scope, path) { this.$watch('locationPath', function(path) {
$location.path(path); $location.path(path);
}); });
this.$watch('$location.path()', function(scope, path) { this.$watch('$location.path()', function(path) {
scope.locationPath = path; scope.locationPath = path;
}); });
</pre> </pre>

View file

@ -19,7 +19,7 @@ function DocsController(scope, $location, $window, $cookies, $filter) {
$location.path('/api').replace(); $location.path('/api').replace();
} }
scope.$watch('$location.path()', function(scope, path) { scope.$watch('$location.path()', function(path) {
// ignore non-doc links which are used in examples // ignore non-doc links which are used in examples
if (DOCS_PATH.test(path)) { if (DOCS_PATH.test(path)) {
var parts = path.split('/'); var parts = path.split('/');

View file

@ -212,9 +212,10 @@ angularDirective("ng:bind", function(expression, element){
element.addClass('ng-binding'); element.addClass('ng-binding');
return ['$exceptionHandler', '$parse', '$element', function($exceptionHandler, $parse, element) { return ['$exceptionHandler', '$parse', '$element', function($exceptionHandler, $parse, element) {
var exprFn = $parse(expression), var exprFn = $parse(expression),
lastValue = Number.NaN; lastValue = Number.NaN,
scope = this;
this.$watch(function(scope) { scope.$watch(function() {
// TODO(misko): remove error handling https://github.com/angular/angular.js/issues/347 // TODO(misko): remove error handling https://github.com/angular/angular.js/issues/347
var value, html, isHtml, isDomElement, var value, html, isHtml, isDomElement,
hadOwnElement = scope.hasOwnProperty('$element'), hadOwnElement = scope.hasOwnProperty('$element'),
@ -305,8 +306,10 @@ angularDirective("ng:bind-template", function(expression, element){
element.addClass('ng-binding'); element.addClass('ng-binding');
var templateFn = compileBindTemplate(expression); var templateFn = compileBindTemplate(expression);
return function(element) { return function(element) {
var lastValue; var lastValue,
this.$watch(function(scope) { scope = this;
scope.$watch(function() {
var value = templateFn(scope, element, true); var value = templateFn(scope, element, true);
if (value != lastValue) { if (value != lastValue) {
element.text(value); element.text(value);
@ -391,8 +394,10 @@ angularDirective("ng:bind-template", function(expression, element){
*/ */
angularDirective("ng:bind-attr", function(expression){ angularDirective("ng:bind-attr", function(expression){
return function(element){ return function(element){
var lastValue = {}; var lastValue = {},
this.$watch(function(scope){ scope = this;
scope.$watch(function() {
var values = scope.$eval(expression); var values = scope.$eval(expression);
for(var key in values) { for(var key in values) {
var value = compileBindTemplate(values[key])(scope, element); var value = compileBindTemplate(values[key])(scope, element);
@ -518,7 +523,8 @@ angularDirective("ng:submit", function(expression, element) {
function ngClass(selector) { function ngClass(selector) {
return function(expression, element) { return function(expression, element) {
return function(element) { return function(element) {
this.$watch(expression, function(scope, newVal, oldVal) { var scope = this;
scope.$watch(expression, function(newVal, oldVal) {
if (selector(scope.$index)) { if (selector(scope.$index)) {
if (oldVal && (newVal !== oldVal)) { if (oldVal && (newVal !== oldVal)) {
element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal); element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal);
@ -687,8 +693,9 @@ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
</doc:example> </doc:example>
*/ */
angularDirective("ng:show", function(expression, element){ angularDirective("ng:show", function(expression, element){
return function(element){ return function(element) {
this.$watch(expression, function(scope, value){ var scope = this;
scope.$watch(expression, function(value) {
element.css('display', toBoolean(value) ? '' : 'none'); element.css('display', toBoolean(value) ? '' : 'none');
}); });
}; };
@ -727,8 +734,9 @@ angularDirective("ng:show", function(expression, element){
</doc:example> </doc:example>
*/ */
angularDirective("ng:hide", function(expression, element){ angularDirective("ng:hide", function(expression, element){
return function(element){ return function(element) {
this.$watch(expression, function(scope, value){ var scope = this;
scope.$watch(expression, function(value) {
element.css('display', toBoolean(value) ? 'none' : ''); element.css('display', toBoolean(value) ? 'none' : '');
}); });
}; };
@ -768,7 +776,8 @@ angularDirective("ng:hide", function(expression, element){
*/ */
angularDirective("ng:style", function(expression, element) { angularDirective("ng:style", function(expression, element) {
return function(element) { return function(element) {
this.$watch(expression, function(scope, newStyles, oldStyles) { var scope = this;
scope.$watch(expression, function(newStyles, oldStyles) {
if (oldStyles && (newStyles !== oldStyles)) { if (oldStyles && (newStyles !== oldStyles)) {
forEach(oldStyles, function(val, style) { element.css(style, '');}); forEach(oldStyles, function(val, style) { element.css(style, '');});
} }

View file

@ -377,7 +377,7 @@ function $FormFactoryProvider() {
// Set the state to something we know will change to get the process going. // Set the state to something we know will change to get the process going.
widget.$modelValue = Number.NaN; widget.$modelValue = Number.NaN;
// watch for scope changes and update the view appropriately // watch for scope changes and update the view appropriately
modelScope.$watch(scopeGet, function(scope, value) { modelScope.$watch(scopeGet, function(value) {
if (!equals(widget.$modelValue, value)) { if (!equals(widget.$modelValue, value)) {
widget.$modelValue = value; widget.$modelValue = value;
widget.$parseModel ? widget.$parseModel() : (widget.$viewValue = value); widget.$parseModel ? widget.$parseModel() : (widget.$viewValue = value);

View file

@ -207,7 +207,7 @@ function $RootScopeProvider(){
scope.counter = 0; scope.counter = 0;
expect(scope.counter).toEqual(0); expect(scope.counter).toEqual(0);
scope.$watch('name', function(scope, newValue, oldValue) { counter = counter + 1; }); scope.$watch('name', function(newValue, oldValue) { counter = counter + 1; });
expect(scope.counter).toEqual(0); expect(scope.counter).toEqual(0);
scope.$digest(); scope.$digest();
@ -231,22 +231,26 @@ function $RootScopeProvider(){
* the `watchExpression` changes. * the `watchExpression` changes.
* *
* - `string`: Evaluated as {@link guide/dev_guide.expressions expression} * - `string`: Evaluated as {@link guide/dev_guide.expressions expression}
* - `function(scope, newValue, oldValue)`: called with current `scope` an previous and * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters.
* current values as parameters.
* @returns {function()} Returns a deregistration function for this listener. * @returns {function()} Returns a deregistration function for this listener.
*/ */
$watch: function(watchExp, listener) { $watch: function(watchExp, listener) {
var scope = this, var scope = this,
get = compileToFn(watchExp, 'watch'), get = compileToFn(watchExp, 'watch'),
listenFn = compileToFn(listener || noop, 'listener'),
array = scope.$$watchers, array = scope.$$watchers,
watcher = { watcher = {
fn: listenFn, fn: listener,
last: initWatchVal, last: initWatchVal,
get: get, get: get,
exp: watchExp exp: watchExp
}; };
// in the case user pass string, we need to compile it, do we really need this ?
if (!isFunction(listener)) {
var listenFn = compileToFn(listener || noop, 'listener');
watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
}
if (!array) { if (!array) {
array = scope.$$watchers = []; array = scope.$$watchers = [];
} }
@ -341,7 +345,7 @@ function $RootScopeProvider(){
if ((value = watch.get(current)) !== (last = watch.last) && !equals(value, last)) { if ((value = watch.get(current)) !== (last = watch.last) && !equals(value, last)) {
dirty = true; dirty = true;
watch.last = copy(value); watch.last = copy(value);
watch.fn(current, value, ((last === initWatchVal) ? value : last)); watch.fn(value, ((last === initWatchVal) ? value : last), current);
if (ttl < 5) { if (ttl < 5) {
logIdx = 4 - ttl; logIdx = 4 - ttl;
if (!watchLog[logIdx]) watchLog[logIdx] = []; if (!watchLog[logIdx]) watchLog[logIdx] = [];

View file

@ -99,7 +99,7 @@ angularWidget('form', function(form){
watch('valid'); watch('valid');
watch('invalid'); watch('invalid');
function watch(name) { function watch(name) {
form.$watch('$' + name, function(scope, value) { form.$watch('$' + name, function(value) {
formElement[value ? 'addClass' : 'removeClass']('ng-' + name); formElement[value ? 'addClass' : 'removeClass']('ng-' + name);
}); });
} }

View file

@ -796,7 +796,7 @@ angularWidget('input', function(inputElement){
}); });
forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) { forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) {
widget.$watch('$' + name, function(scope, value) { widget.$watch('$' + name, function(value) {
inputElement[value ? 'addClass' : 'removeClass']('ng-' + name); inputElement[value ? 'addClass' : 'removeClass']('ng-' + name);
}); });
}); });
@ -870,7 +870,7 @@ function watchElementProperty(modelScope, widget, name, element) {
!!element[0].attributes[name]) !!element[0].attributes[name])
: element.attr(name); : element.attr(name);
if (bindAttr[name] && match) { if (bindAttr[name] && match) {
modelScope.$watch(match[1], function(scope, value){ modelScope.$watch(match[1], function(value) {
widget['$' + name] = isBoolean ? !!value : value; widget['$' + name] = isBoolean ? !!value : value;
widget.$emit('$validate'); widget.$emit('$validate');
widget.$render && widget.$render(); widget.$render && widget.$render();

View file

@ -167,7 +167,7 @@ angularWidget('select', function(element){
}); });
forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) { forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) {
widget.$watch('$' + name, function(scope, value) { widget.$watch('$' + name, function(value) {
selectElement[value ? 'addClass' : 'removeClass']('ng-' + name); selectElement[value ? 'addClass' : 'removeClass']('ng-' + name);
}); });
}); });

View file

@ -111,7 +111,7 @@ angularWidget('ng:include', function(element){
var includeScope = scope.$eval(scopeExp); var includeScope = scope.$eval(scopeExp);
if (includeScope) return includeScope.$id; if (includeScope) return includeScope.$id;
}, incrementChange); }, incrementChange);
this.$watch(function() {return changeCounter;}, function(scope, newChangeCounter) { this.$watch(function() {return changeCounter;}, function(newChangeCounter) {
var src = scope.$eval(srcExp), var src = scope.$eval(srcExp),
useScope = scope.$eval(scopeExp); useScope = scope.$eval(scopeExp);
@ -233,8 +233,9 @@ angularWidget('ng:switch', function(element) {
var changeCounter = 0; var changeCounter = 0;
var childScope; var childScope;
var selectedTemplate; var selectedTemplate;
var scope = this;
this.$watch(watchExpr, function(scope, value) { this.$watch(watchExpr, function(value) {
element.html(''); element.html('');
if ((selectedTemplate = casesTemplate[value] || defaultCaseTemplate)) { if ((selectedTemplate = casesTemplate[value] || defaultCaseTemplate)) {
changeCounter++; changeCounter++;
@ -577,7 +578,7 @@ angularWidget('ng:view', function(element) {
changeCounter++; changeCounter++;
}); });
this.$watch(function() {return changeCounter;}, function(scope, newChangeCounter) { this.$watch(function() {return changeCounter;}, function(newChangeCounter) {
var template = $route.current && $route.current.template; var template = $route.current && $route.current.template;
function clearContent() { function clearContent() {
@ -802,7 +803,7 @@ angularWidget('ng:pluralize', function(element) {
} else { } else {
return ''; return '';
} }
}, function(scope, newVal) { }, function(newVal) {
element.text(newVal); element.text(newVal);
}); });
}]; }];

View file

@ -17,7 +17,7 @@ describe('compiler', function() {
observe: function(expression, element){ observe: function(expression, element){
return function() { return function() {
this.$watch(expression, function(scope, val){ this.$watch(expression, function(val) {
if (val) if (val)
log += ":" + val; log += ":" + val;
}); });

View file

@ -372,7 +372,7 @@ describe('$route', function() {
function FooCtrl($scope) { function FooCtrl($scope) {
$scope.$watch(function() { $scope.$watch(function() {
return $route.current.params; return $route.current.params;
}, function(scope, value) { }, function(value) {
routeParams(value); routeParams(value);
}); });
} }

View file

@ -68,7 +68,7 @@ describe('Scope', function() {
expect(spy).not.wasCalled(); expect(spy).not.wasCalled();
$rootScope.name = 'misko'; $rootScope.name = 'misko';
$rootScope.$digest(); $rootScope.$digest();
expect(spy).wasCalledWith($rootScope, 'misko', undefined); expect(spy).wasCalledWith('misko', undefined, $rootScope);
})); }));
@ -156,9 +156,9 @@ describe('Scope', function() {
it('should repeat watch cycle while model changes are identified', inject(function($rootScope) { it('should repeat watch cycle while model changes are identified', inject(function($rootScope) {
var log = ''; var log = '';
$rootScope.$watch('c', function(self, v) {self.d = v; log+='c'; }); $rootScope.$watch('c', function(v) {$rootScope.d = v; log+='c'; });
$rootScope.$watch('b', function(self, v) {self.c = v; log+='b'; }); $rootScope.$watch('b', function(v) {$rootScope.c = v; log+='b'; });
$rootScope.$watch('a', function(self, v) {self.b = v; log+='a'; }); $rootScope.$watch('a', function(v) {$rootScope.b = v; log+='a'; });
$rootScope.$digest(); $rootScope.$digest();
log = ''; log = '';
$rootScope.a = 1; $rootScope.a = 1;
@ -182,8 +182,8 @@ describe('Scope', function() {
it('should prevent infinite recursion and print watcher expression',inject( it('should prevent infinite recursion and print watcher expression',inject(
function($rootScope) { function($rootScope) {
$rootScope.$watch('a', function(self) {self.b++;}); $rootScope.$watch('a', function() {$rootScope.b++;});
$rootScope.$watch('b', function(self) {self.a++;}); $rootScope.$watch('b', function() {$rootScope.a++;});
$rootScope.a = $rootScope.b = 0; $rootScope.a = $rootScope.b = 0;
expect(function() { expect(function() {
@ -200,8 +200,8 @@ describe('Scope', function() {
it('should prevent infinite recursion and print print watcher function name or body', it('should prevent infinite recursion and print print watcher function name or body',
inject(function($rootScope) { inject(function($rootScope) {
$rootScope.$watch(function watcherA() {return $rootScope.a;}, function(self) {self.b++;}); $rootScope.$watch(function watcherA() {return $rootScope.a;}, function() {$rootScope.b++;});
$rootScope.$watch(function() {return $rootScope.b;}, function(self) {self.a++;}); $rootScope.$watch(function() {return $rootScope.b;}, function() {$rootScope.a++;});
$rootScope.a = $rootScope.b = 0; $rootScope.a = $rootScope.b = 0;
try { try {
@ -229,11 +229,11 @@ describe('Scope', function() {
var log = ''; var log = '';
$rootScope.a = []; $rootScope.a = [];
$rootScope.b = {}; $rootScope.b = {};
$rootScope.$watch('a', function(scope, value) { $rootScope.$watch('a', function(value) {
log +='.'; log +='.';
expect(value).toBe($rootScope.a); expect(value).toBe($rootScope.a);
}); });
$rootScope.$watch('b', function(scope, value) { $rootScope.$watch('b', function(value) {
log +='!'; log +='!';
expect(value).toBe($rootScope.b); expect(value).toBe($rootScope.b);
}); });
@ -427,7 +427,7 @@ describe('Scope', function() {
it('should apply expression with full lifecycle', inject(function($rootScope) { it('should apply expression with full lifecycle', inject(function($rootScope) {
var log = ''; var log = '';
var child = $rootScope.$new(); var child = $rootScope.$new();
$rootScope.$watch('a', function(scope, a) { log += '1'; }); $rootScope.$watch('a', function(a) { log += '1'; });
child.$apply('$parent.a=0'); child.$apply('$parent.a=0');
expect(log).toEqual('1'); expect(log).toEqual('1');
})); }));
@ -440,7 +440,7 @@ describe('Scope', function() {
inject(function($rootScope, $exceptionHandler, $log) { inject(function($rootScope, $exceptionHandler, $log) {
var log = ''; var log = '';
var child = $rootScope.$new(); var child = $rootScope.$new();
$rootScope.$watch('a', function(scope, a) { log += '1'; }); $rootScope.$watch('a', function(a) { log += '1'; });
$rootScope.a = 0; $rootScope.a = 0;
child.$apply(function() { throw new Error('MyError'); }); child.$apply(function() { throw new Error('MyError'); });
expect(log).toEqual('1'); expect(log).toEqual('1');
@ -520,7 +520,7 @@ describe('Scope', function() {
function($rootScope) { function($rootScope) {
var childScope2 = $rootScope.$new(); var childScope2 = $rootScope.$new();
childScope2.$apply(function() { childScope2.$apply(function() {
childScope2.$watch('x', function(scope, newVal, oldVal) { childScope2.$watch('x', function(newVal, oldVal) {
if (newVal !== oldVal) { if (newVal !== oldVal) {
childScope2.$apply(); childScope2.$apply();
} }