diff --git a/src/ng/compile.js b/src/ng/compile.js
index 30f40539a9bc..a20d1d170731 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -3500,7 +3500,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
});
function recordChanges(key, currentValue, previousValue) {
- if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
+ if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
+ // eslint-disable-next-line no-self-compare
+ (currentValue === currentValue || previousValue === previousValue)) {
// If we have not already scheduled the top level onChangesQueue handler then do so now
if (!onChangesQueue) {
scope.$$postDigest(flushOnChangesQueue);
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index 72015a3559ad..77a72d24f5c0 100755
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -4327,6 +4327,40 @@ describe('$compile', function() {
});
+ it('should not call `$onChanges` twice even when the initial value is `NaN`', function() {
+ var onChangesSpy = jasmine.createSpy('$onChanges');
+
+ module(function($compileProvider) {
+ $compileProvider.component('test', {
+ bindings: {prop: '<', attr: '@'},
+ controller: function TestController() {
+ this.$onChanges = onChangesSpy;
+ }
+ });
+ });
+
+ inject(function($compile, $rootScope) {
+ var template = '' +
+ '';
+ $rootScope.a = 'foo';
+ $rootScope.b = NaN;
+
+ element = $compile(template)($rootScope);
+ $rootScope.$digest();
+
+ expect(onChangesSpy).toHaveBeenCalledTimes(2);
+ expect(onChangesSpy.calls.argsFor(0)[0]).toEqual({
+ prop: jasmine.objectContaining({currentValue: 'foo'}),
+ attr: jasmine.objectContaining({currentValue: 'foo'})
+ });
+ expect(onChangesSpy.calls.argsFor(1)[0]).toEqual({
+ prop: jasmine.objectContaining({currentValue: NaN}),
+ attr: jasmine.objectContaining({currentValue: 'NaN'})
+ });
+ });
+ });
+
+
it('should only trigger one extra digest however many controllers have changes', function() {
var log = [];
function TestController1() { }