Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

feat(ngDefer): new ngDefer directive to defer compilation until condition is met #16552

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions angularFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
2 changes: 2 additions & 0 deletions src/AngularPublic.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ngClassOddDirective,
ngCloakDirective,
ngControllerDirective,
ngDeferDirective,
ngFormDirective,
ngHideDirective,
ngIfDirective,
Expand Down Expand Up @@ -187,6 +188,7 @@ function publishExternalAPI(angular) {
ngClassOdd: ngClassOddDirective,
ngCloak: ngCloakDirective,
ngController: ngControllerDirective,
ngDefer: ngDeferDirective,
ngForm: ngFormDirective,
ngHide: ngHideDirective,
ngIf: ngIfDirective,
Expand Down
56 changes: 56 additions & 0 deletions src/ng/directive/ngDefer.js
Original file line number Diff line number Diff line change
@@ -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
*
<example name="ng-defer">
<file name="index.html">
<div ng-init="run = false">
<div>Normal: {{1 + 2}}</div>
<div ng-defer="run == true">Ignored: {{1 + 2}}</div>
<a id="run-link" ng-click="run = true">Run deferred</a>
</div>
</file>
<file name="protractor.js" type="protractor">
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');
});
</file>
</example>
*/
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);
}
});
}
};
}];
44 changes: 44 additions & 0 deletions test/ng/directive/ngDeferSpec.js
Original file line number Diff line number Diff line change
@@ -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('<div ng-init="run = false">' +
' <span id="s1">{{a}}</span>' +
' <span id="s2" ng-bind="b"></span>' +
' <div foo="{{a}}" ng-defer="run == true">' +
' <span ng-bind="a"></span>{{b}}' +
' </div>' +
' <span id="s3">{{a}}</span>' +
' <span id="s4" ng-bind="b"></span>' +
' <a id="run-link" ng-click="run = true">Run deferred</a>' +
'</div>')($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');
})
);
});