From b0c0f3bafdaaab219da70fe8fabb5684c09ed143 Mon Sep 17 00:00:00 2001 From: Zach Bjornson Date: Mon, 23 May 2016 18:31:21 -0700 Subject: [PATCH] feat($compile): backport $doCheck Backuport ngDoCheck from Angular 2. --- src/ng/compile.js | 20 ++++++++++++++++++- test/ng/compileSpec.js | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/ng/compile.js b/src/ng/compile.js index 2780fd76435d..78635edd9642 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -300,6 +300,12 @@ * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a * component such as cloning the bound value to prevent accidental mutation of the outer value. + * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on + * changes. Any actions that you wish to take in response to the changes that you detect must be + * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook + * could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not + * be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments; + * if detecting changes, you must store the previous value(s) for comparison to the current values. * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent @@ -2483,6 +2489,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (isFunction(controllerInstance.$onInit)) { controllerInstance.$onInit(); } + if (isFunction(controllerInstance.$doCheck)) { + controllerInstance.$doCheck(); + } if (isFunction(controllerInstance.$onDestroy)) { controllerScope.$on('$destroy', function callOnDestroyHook() { controllerInstance.$onDestroy(); @@ -3131,7 +3140,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { forEach(bindings, function initializeBinding(definition, scopeName) { var attrName = definition.attrName, optional = definition.optional, - mode = definition.mode, // @, =, or & + mode = definition.mode, // @, =, <, or & lastValue, parentGet, parentSet, compare, removeWatch; @@ -3243,6 +3252,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } }); + if (isFunction(destination.$doCheck)) { + var doCheckWatch = scope.$watch(triggerDoCheckHook); + removeWatchCollection.push(doCheckWatch); + } + function recordChanges(key, currentValue, previousValue) { if (isFunction(destination.$onChanges) && currentValue !== previousValue) { // If we have not already scheduled the top level onChangesQueue handler then do so now @@ -3270,6 +3284,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { changes = undefined; } + function triggerDoCheckHook() { + destination.$doCheck(); + } + return { initialChanges: initialChanges, removeWatches: removeWatchCollection.length && function removeWatches() { diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index b1dd097e99a8..0e76fcaa9f59 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -3754,6 +3754,51 @@ describe('$compile', function() { }); }); + describe('$doCheck', function() { + it('should call `$doCheck`, if provided, for each digest cycle, after $onChanges and $onInit', function() { + var log = []; + + function TestController() { } + TestController.prototype.$doCheck = function() { log.push('$doCheck'); }; + TestController.prototype.$onChanges = function() { log.push('$onChanges'); }; + TestController.prototype.$onInit = function() { log.push('$onInit'); }; + + angular.module('my', []) + .component('dcc', { + controller: TestController, + bindings: { 'prop1': '<' } + }); + + module('my'); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + expect(log).toEqual([ + '$onChanges', + '$onInit', + '$doCheck' + ]); + + // Clear log + log = []; + + $rootScope.$apply() + expect(log).toEqual([ + '$doCheck', + '$doCheck' + ]); + + // Clear log + log = []; + + $rootScope.$apply('val = 2'); + expect(log).toEqual([ + '$doCheck', + '$onChanges', + '$doCheck' + ]); + }); + }); + }); describe('$onChanges', function() {