diff --git a/angularFiles.js b/angularFiles.js index 0233722adfc4..55c22e6342cb 100644 --- a/angularFiles.js +++ b/angularFiles.js @@ -64,6 +64,7 @@ var angularFiles = { 'src/ng/directive/ngCloak.js', 'src/ng/directive/ngController.js', 'src/ng/directive/ngCsp.js', + 'src/ng/directive/ngDefer.js', 'src/ng/directive/ngEventDirs.js', 'src/ng/directive/ngIf.js', 'src/ng/directive/ngInclude.js', diff --git a/src/AngularPublic.js b/src/AngularPublic.js index c18889911a50..c6d0954ece26 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -20,6 +20,7 @@ ngClassOddDirective, ngCloakDirective, ngControllerDirective, + ngDeferDirective, ngFormDirective, ngHideDirective, ngIfDirective, @@ -187,6 +188,7 @@ function publishExternalAPI(angular) { ngClassOdd: ngClassOddDirective, ngCloak: ngCloakDirective, ngController: ngControllerDirective, + ngDefer: ngDeferDirective, ngForm: ngFormDirective, ngHide: ngHideDirective, ngIf: ngIfDirective, diff --git a/src/ng/directive/ngDefer.js b/src/ng/directive/ngDefer.js new file mode 100644 index 000000000000..463713164c1a --- /dev/null +++ b/src/ng/directive/ngDefer.js @@ -0,0 +1,56 @@ +'use strict'; + +/** + * @ngdoc directive + * @name ngDefer + * @restrict A + * @priority 600 + * @element ANY + * + * @description + * The `ngDefer` directive pauses AngularJS compilation at the current DOM element, + * including directives on the element itself that have a lower priority than + * `ngDefer`. + * It will continue the process at the very same element for any directives with lower + * priority once the condition provided to the `tiDefer` attribute evalutes to a trueish + * value. + * This is useful to defer compilation of entire sub-trees of HTML to when it is really + * needed, e.g. when the sub-tree becomes visible. This can be very useful in progressive + * enhancement context and generally to improve page initialization time by splitting + * compilation into smaller chunks that get executed on an as-needed basis. + * + * @example + * + + +
+
Normal: {{1 + 2}}
+
Ignored: {{1 + 2}}
+ Run deferred +
+
+ + it('should defer ng-bind', function() { + expect(element(by.binding('1 + 2')).getText()).toContain('3'); + expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); + element(by.css('#run-link')).click(); + expect(element.all(by.css('div')).last().getText()).toContain('3'); + }); + +
+ */ +var ngDeferDirective = ['$compile', function($compile) { + return { + restrict: 'A', + terminal: true, + priority: 600, + link: function($scope, $element, $attr) { + var unregisterWatch = $scope.$watch($attr.ngDefer, function ngDeferWatchAction(newVal) { + if (newVal) { + unregisterWatch(); + $compile($element, undefined, 600)($scope); + } + }); + } + }; +}]; diff --git a/test/ng/directive/ngDeferSpec.js b/test/ng/directive/ngDeferSpec.js new file mode 100644 index 000000000000..b0f3d349dff8 --- /dev/null +++ b/test/ng/directive/ngDeferSpec.js @@ -0,0 +1,44 @@ +'use strict'; + + +describe('ngDefer', function() { + var element; + + + afterEach(function() { + dealoc(element); + }); + + + it('should defer compilation of the owning element and its children', + inject(function($rootScope, $compile) { + element = $compile('
' + + ' {{a}}' + + ' ' + + '
' + + ' {{b}}' + + '
' + + ' {{a}}' + + ' ' + + ' Run deferred' + + '
')($rootScope); + $rootScope.a = 'one'; + $rootScope.b = 'two'; + $rootScope.$digest(); + // Bindings not contained by ng-non-bindable should resolve. + var spans = element.find('span'); + expect(spans.eq(0).text()).toEqual('one'); + expect(spans.eq(1).text()).toEqual('two'); + expect(spans.eq(3).text()).toEqual('one'); + expect(spans.eq(4).text()).toEqual('two'); + // Bindings contained by ng-non-bindable should be left alone. + var ngDeferDiv = element.find('div'); + expect(ngDeferDiv.attr('foo')).toEqual('{{a}}'); + expect(trim(ngDeferDiv.text())).toEqual('{{b}}'); + var link = element.find('a'); + browserTrigger(link, 'click'); + expect(ngDeferDiv.attr('foo')).toEqual('one'); + expect(trim(ngDeferDiv.text())).toEqual('onetwo'); + }) + ); +});