Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a93f03d

Browse files
petebacondarwinvojtajina
authored andcommitted
feat($compile/ngBind): allow disabling binding info
The compiler and ngBind directives add binding information (`ng-binding` CSS class and `$binding` data property) to elements when they are bound to the scope. This is only to aid testing and debugging for tools such as Protractor and Batarang. In production this is unnecessary and add a performance penalty. This can be now disabled by calling `$compileProvider.debugInfoEnabled(false)` in a module `config` block: ``` someModule.config(['$compileProvider', function($compileProvider) { $compileProvider.debugInfoEnabled(false); }]); ``` In the bench/apps/largetable-bp benchmark this change, with debug info disabled, improved by ~140ms, that is 10%. Measuring the "create" phase, 25 loops, mean time ~1340ms -> ~1200ms. We were storing the whole `interpolationFn` in the `$binding` data on elements but this function was bringing a lot of closure variables with it and so was consuming unwanted amounts of memory. Now we are only storing the parsed interpolation expressions from the binding (i.e. the values of `interpolationFn.expressions`). BREAKING CHANGE: The value of `$binding` data property on an element is always an array now and the expressions do not include the curly braces `{{ ... }}`.
1 parent 2ae4f40 commit a93f03d

File tree

5 files changed

+84
-50
lines changed

5 files changed

+84
-50
lines changed

src/ng/compile.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,34 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
668668
}
669669
};
670670

671+
/**
672+
* @ngdoc method
673+
* @name $compileProvider#debugInfoEnabled
674+
*
675+
* @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
676+
* current debugInfoEnabled state
677+
* @returns {*} current value if used as getter or itself (chaining) if used as setter
678+
*
679+
* @kind function
680+
*
681+
* @description
682+
* Call this method to enable/disable various debug runtime information in the compiler such as adding
683+
* binding information and a reference to the current scope on to DOM elements.
684+
* If enabled, the compiler will add the following to DOM elements that have been bound to the scope
685+
* * `ng-binding` CSS class
686+
* * `$binding` data property containing an array of the binding expressions
687+
*
688+
* The default value is true.
689+
*/
690+
var debugInfoEnabled = true;
691+
this.debugInfoEnabled = function(enabled) {
692+
if(isDefined(enabled)) {
693+
debugInfoEnabled = enabled;
694+
return this;
695+
}
696+
return debugInfoEnabled;
697+
};
698+
671699
this.$get = [
672700
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
673701
'$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
@@ -867,6 +895,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
867895
},
868896
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
869897

898+
compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo(element, binding) {
899+
element
900+
.addClass('ng-binding')
901+
.data('$binding',
902+
(element.data('$binding') || []).concat(binding.expressions || [binding])
903+
);
904+
} : noop;
870905

871906
return compile;
872907

@@ -1950,17 +1985,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
19501985
directives.push({
19511986
priority: 0,
19521987
compile: function textInterpolateCompileFn(templateNode) {
1953-
// when transcluding a template that has bindings in the root
1954-
// then we don't have a parent and should do this in the linkFn
1955-
var parent = templateNode.parent(), hasCompileParent = parent.length;
1956-
if (hasCompileParent) safeAddClass(templateNode.parent(), 'ng-binding');
1957-
19581988
return function textInterpolateLinkFn(scope, node) {
1959-
var parent = node.parent(),
1960-
bindings = parent.data('$binding') || [];
1961-
bindings.push(interpolateFn);
1962-
parent.data('$binding', bindings);
1963-
if (!hasCompileParent) safeAddClass(parent, 'ng-binding');
1989+
var parent = node.parent();
1990+
compile.$$addBindingInfo(parent, interpolateFn);
19641991
scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
19651992
node[0].nodeValue = value;
19661993
});

src/ng/directive/ngBind.js

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,22 @@
5151
</file>
5252
</example>
5353
*/
54-
var ngBindDirective = ngDirective({
55-
compile: function ngBindCompile(templateElement) {
56-
templateElement.addClass('ng-binding');
57-
58-
return function ngBindLink(scope, element, attr) {
59-
element.data('$binding', attr.ngBind);
60-
element = element[0];
61-
62-
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
63-
// We are purposefully using == here rather than === because we want to
64-
// catch when value is "null or undefined"
65-
// jshint -W041
66-
element.textContent = (value == undefined ? '' : value);
67-
});
68-
};
69-
}
70-
});
54+
var ngBindDirective = ['$compile', function($compile) {
55+
return {
56+
restrict: 'AC',
57+
compile: function(templateElement) {
58+
return function (scope, element, attr) {
59+
$compile.$$addBindingInfo(element, attr.ngBind);
60+
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
61+
// We are purposefully using == here rather than === because we want to
62+
// catch when value is "null or undefined"
63+
// jshint -W041
64+
element.text(value == undefined ? '' : value);
65+
});
66+
};
67+
}
68+
};
69+
}];
7170

7271

7372
/**
@@ -121,11 +120,10 @@ var ngBindDirective = ngDirective({
121120
</file>
122121
</example>
123122
*/
124-
var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
123+
var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
125124
return function(scope, element, attr) {
126-
// TODO: move this to scenario runner
127125
var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
128-
element.addClass('ng-binding').data('$binding', interpolateFn);
126+
$compile.$$addBindingInfo(element, interpolateFn);
129127
attr.$observe('ngBindTemplate', function(value) {
130128
element.text(value);
131129
});
@@ -178,14 +176,13 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
178176
</file>
179177
</example>
180178
*/
181-
var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
179+
var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
182180
return {
183181
restrict: 'A',
184-
compile: function ngBindCompile(tElement, tAttrs) {
185-
tElement.addClass('ng-binding');
182+
compile: function (tElement, tAttrs) {
186183

187-
return function ngBindLink(scope, element, attr) {
188-
element.data('$binding', attr.ngBindHtml);
184+
return function (scope, element, attr) {
185+
$compile.$$addBindingInfo(element, attr.ngBindHtml);
189186
var ngBindHtmlGetter = $parse(attr.ngBindHtml);
190187
var ngBindHtmlWatch = $parse(attr.ngBindHtml, function getStringValue(value) {
191188
return (value || '').toString();

src/ngScenario/Scenario.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,6 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
304304
var element = windowJquery(this),
305305
bindings;
306306
if (bindings = element.data('$binding')) {
307-
if (!angular.isArray(bindings)) {
308-
bindings = [bindings];
309-
}
310307
for(var expressions = [], binding, j=0, jj=bindings.length; j<jj; j++) {
311308
binding = bindings[j];
312309

test/ng/compileSpec.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,14 +2491,34 @@ describe('$compile', function() {
24912491
}));
24922492
});
24932493

2494-
it('should decorate the binding with ng-binding and interpolation function', inject(
2495-
function($compile, $rootScope) {
2494+
describe('decorating with binding info', function() {
2495+
2496+
it('should not occur if `debugInfoEnabled` is false', function() {
2497+
module(function($compileProvider) {
2498+
$compileProvider.debugInfoEnabled(false);
2499+
});
2500+
2501+
inject(function($compile, $rootScope) {
24962502
element = $compile('<div>{{1+2}}</div>')($rootScope);
2497-
expect(element.hasClass('ng-binding')).toBe(true);
2498-
expect(element.data('$binding')[0].exp).toEqual('{{1+2}}');
2499-
}));
2503+
expect(element.hasClass('ng-binding')).toBe(false);
2504+
expect(element.data('$binding')).toBeUndefined();
2505+
});
2506+
});
25002507

25012508

2509+
it('should occur if `debugInfoEnabled` is true', function() {
2510+
module(function($compileProvider) {
2511+
$compileProvider.debugInfoEnabled(true);
2512+
});
2513+
2514+
inject(function($compile, $rootScope) {
2515+
element = $compile('<div>{{1+2}}</div>')($rootScope);
2516+
expect(element.hasClass('ng-binding')).toBe(true);
2517+
expect(element.data('$binding')).toEqual(['1+2']);
2518+
});
2519+
});
2520+
});
2521+
25022522
it('should observe interpolated attrs', inject(function($rootScope, $compile) {
25032523
$compile('<div some-attr="{{value}}" observer></div>')($rootScope);
25042524

test/ng/directive/ngBindSpec.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,6 @@ describe('ngBind*', function() {
122122

123123
describe('ngBindHtml', function() {
124124

125-
it('should add ng-binding class to the element in compile phase', inject(function($compile) {
126-
var element = jqLite('<div ng-bind-html="myHtml"></div>');
127-
$compile(element);
128-
expect(element.hasClass('ng-binding')).toBe(true);
129-
}));
130-
131-
132125
describe('SCE disabled', function() {
133126
beforeEach(function() {
134127
module(function($sceProvider) { $sceProvider.enabled(false); });

0 commit comments

Comments
 (0)