diff --git a/src/.eslintrc.json b/src/.eslintrc.json index 4cf6b0f95e00..4c240b733bf9 100644 --- a/src/.eslintrc.json +++ b/src/.eslintrc.json @@ -69,6 +69,7 @@ "arrayRemove": false, "copy": false, "shallowCopy": false, + "simpleCompare": false, "equals": false, "csp": false, "concat": false, diff --git a/src/Angular.js b/src/Angular.js index af6379e22ad1..7f539e81fb52 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -62,6 +62,7 @@ includes, arrayRemove, copy, + simpleCompare, equals, csp, jq, @@ -1024,6 +1025,10 @@ function copy(source, destination, maxDepth) { } +// eslint-disable-next-line no-self-compare +function simpleCompare(a, b) { return a === b || (a !== a && b !== b); } + + /** * @ngdoc function * @name angular.equals @@ -1104,7 +1109,7 @@ function equals(o1, o2) { } } else if (isDate(o1)) { if (!isDate(o2)) return false; - return equals(o1.getTime(), o2.getTime()); + return simpleCompare(o1.getTime(), o2.getTime()); } else if (isRegExp(o1)) { if (!isRegExp(o2)) return false; return o1.toString() === o2.toString(); diff --git a/src/ng/compile.js b/src/ng/compile.js index 7c89444fcb5a..5c1f255849b8 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -3434,8 +3434,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (parentGet.literal) { compare = equals; } else { - // eslint-disable-next-line no-self-compare - compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }; + compare = simpleCompare; } parentSet = parentGet.assign || function() { // reset the change, or we will throw this exception on every $digest @@ -3510,9 +3509,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { }); function recordChanges(key, currentValue, previousValue) { - if (isFunction(destination.$onChanges) && currentValue !== previousValue && - // eslint-disable-next-line no-self-compare - (currentValue === currentValue || previousValue === previousValue)) { + if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) { // If we have not already scheduled the top level onChangesQueue handler then do so now if (!onChangesQueue) { scope.$$postDigest(flushOnChangesQueue); diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index a1f47678e491..063ebf05f003 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -890,8 +890,8 @@ function setupModelWatcher(ctrl) { // -> scope value did not change since the last digest as // ng-change executes in apply phase // 4. view should be changed back to 'a' - ctrl.$$scope.$watch(function ngModelWatch() { - var modelValue = ctrl.$$ngModelGet(ctrl.$$scope); + ctrl.$$scope.$watch(function ngModelWatch(scope) { + var modelValue = ctrl.$$ngModelGet(scope); // if scope model value and ngModel value are out of sync // TODO(perf): why not move this to the action fn? diff --git a/src/ng/parse.js b/src/ng/parse.js index 98179e8f549b..8956c618fa4c 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -769,15 +769,13 @@ function isConstant(ast) { return ast.constant; } -function ASTCompiler(astBuilder, $filter) { - this.astBuilder = astBuilder; +function ASTCompiler($filter) { this.$filter = $filter; } ASTCompiler.prototype = { - compile: function(expression) { + compile: function(ast) { var self = this; - var ast = this.astBuilder.ast(expression); this.state = { nextId: 0, filters: {}, @@ -832,8 +830,6 @@ ASTCompiler.prototype = { ifDefined, plusFn); this.state = this.stage = undefined; - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); return fn; }, @@ -1236,15 +1232,13 @@ ASTCompiler.prototype = { }; -function ASTInterpreter(astBuilder, $filter) { - this.astBuilder = astBuilder; +function ASTInterpreter($filter) { this.$filter = $filter; } ASTInterpreter.prototype = { - compile: function(expression) { + compile: function(ast) { var self = this; - var ast = this.astBuilder.ast(expression); findConstantAndWatchExpressions(ast, self.$filter); var assignable; var assign; @@ -1283,8 +1277,6 @@ ASTInterpreter.prototype = { if (inputs) { fn.inputs = inputs; } - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); return fn; }, @@ -1613,20 +1605,21 @@ ASTInterpreter.prototype = { /** * @constructor */ -var Parser = function Parser(lexer, $filter, options) { - this.lexer = lexer; - this.$filter = $filter; - this.options = options; +function Parser(lexer, $filter, options) { this.ast = new AST(lexer, options); - this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) : - new ASTCompiler(this.ast, $filter); -}; + this.astCompiler = options.csp ? new ASTInterpreter($filter) : + new ASTCompiler($filter); +} Parser.prototype = { constructor: Parser, parse: function(text) { - return this.astCompiler.compile(text); + var ast = this.ast.ast(text); + var fn = this.astCompiler.compile(ast); + fn.literal = isLiteral(ast); + fn.constant = isConstant(ast); + return fn; } }; @@ -1942,9 +1935,8 @@ function $ParseProvider() { // Propagate $$watchDelegates other then inputsWatchDelegate useInputs = !parsedExpression.inputs; - if (parsedExpression.$$watchDelegate && - parsedExpression.$$watchDelegate !== inputsWatchDelegate) { - fn.$$watchDelegate = parsedExpression.$$watchDelegate; + if (watchDelegate && watchDelegate !== inputsWatchDelegate) { + fn.$$watchDelegate = watchDelegate; fn.inputs = parsedExpression.inputs; } else if (!interceptorFn.$stateful) { // If there is an interceptor, but no watchDelegate then treat the interceptor like diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 2d01c4ed5f0c..000068c45168 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -5944,6 +5944,30 @@ describe('$compile', function() { })); + // https://github.com/angular/angular.js/issues/15833 + it('should work with ng-model inputs', function() { + var componentScope; + + module(function($compileProvider) { + $compileProvider.directive('undi', function() { + return { + restrict: 'A', + scope: { + undi: '<' + }, + link: function($scope) { componentScope = $scope; } + }; + }); + }); + + inject(function($compile, $rootScope) { + element = $compile('
')($rootScope); + $rootScope.$apply(); + expect(componentScope.undi).toBeDefined(); + }); + }); + + it('should not complain when the isolated scope changes', inject(function() { compile('