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

Commit 52e00a5

Browse files
Jason Bedardjbedard
Jason Bedard
authored andcommitted
perf($compile): remove use of deepEquals and $stateful interceptor from bidi bindings
1 parent 627180f commit 52e00a5

File tree

2 files changed

+144
-28
lines changed

2 files changed

+144
-28
lines changed

src/ng/compile.js

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,9 @@
309309
* to the local value, since it will be impossible to sync them back to the parent scope.
310310
*
311311
* By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
312-
* method is used for tracking changes, and the equality check is based on object identity.
313-
* However, if an object literal or an array literal is passed as the binding expression, the
314-
* equality check is done by value (using the {@link angular.equals} function). It's also possible
315-
* to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
316-
* `$watchCollection`}: use `=*` or `=*attr`
312+
* method is used for tracking changes between the local scope property and the expression passed
313+
* via the attribute. It's also possible to watch the evaluated value shallowly with
314+
* {@link ng.$rootScope.Scope#$watchCollection `$watchCollection`}: use `=*` or `=*attr`.
317315
*
318316
* * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
319317
* expression passed via the attribute `attr`. The expression is evaluated in the context of the
@@ -3535,7 +3533,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
35353533
optional = definition.optional,
35363534
mode = definition.mode, // @, =, <, or &
35373535
lastValue,
3538-
parentGet, parentSet, compare, removeWatch;
3536+
parentGet, removeWatch;
35393537

35403538
switch (mode) {
35413539

@@ -3576,23 +3574,22 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
35763574
if (optional && !attrs[attrName]) break;
35773575

35783576
parentGet = $parse(attrs[attrName]);
3579-
if (parentGet.literal) {
3580-
compare = equals;
3581-
} else {
3582-
compare = simpleCompare;
3583-
}
3584-
parentSet = parentGet.assign || function() {
3577+
var parentSet = parentGet.assign || function() {
35853578
// reset the change, or we will throw this exception on every $digest
35863579
lastValue = destination[scopeName] = parentGet(scope);
35873580
throw $compileMinErr('nonassign',
35883581
'Expression \'{0}\' in attribute \'{1}\' used with directive \'{2}\' is non-assignable!',
35893582
attrs[attrName], attrName, directive.name);
35903583
};
3584+
var childGet = function childGet() { return destination[scopeName]; };
3585+
35913586
lastValue = destination[scopeName] = parentGet(scope);
3592-
var parentValueWatch = function parentValueWatch(parentValue) {
3593-
if (!compare(parentValue, destination[scopeName])) {
3587+
3588+
var bidiWatchAction = function bidiWatchAction(newValues) {
3589+
var parentValue = newValues[0];
3590+
if (!simpleCompare(parentValue, destination[scopeName])) {
35943591
// we are out of sync and need to copy
3595-
if (!compare(parentValue, lastValue)) {
3592+
if (!simpleCompare(parentValue, lastValue)) {
35963593
// parent changed and it has precedence
35973594
destination[scopeName] = parentValue;
35983595
} else {
@@ -3601,13 +3598,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
36013598
}
36023599
}
36033600
lastValue = parentValue;
3604-
return lastValue;
36053601
};
3606-
parentValueWatch.$stateful = true;
36073602
if (definition.collection) {
3608-
removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
3603+
removeWatch = scope.$watchCollection(parentGet, function bidiCollectionWatchAction(parentValue) { destination[scopeName] = parentValue; });
36093604
} else {
3610-
removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
3605+
removeWatch = scope.$watchGroup([parentGet, childGet], bidiWatchAction);
36113606
}
36123607
removeWatchCollection.push(removeWatch);
36133608
break;

test/ng/compileSpec.js

Lines changed: 130 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5416,21 +5416,21 @@ describe('$compile', function() {
54165416

54175417
inject(function($rootScope) {
54185418
compile('<div other-tpl-dir param1="::foo" param2="bar"></div>');
5419-
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '='
5419+
expect(countWatches($rootScope)).toEqual(8); // 4 -> template watch group, 2 -> '='
54205420
$rootScope.$digest();
54215421
expect(element.html()).toBe('1:;2:;3:;4:');
5422-
expect(countWatches($rootScope)).toEqual(6);
5422+
expect(countWatches($rootScope)).toEqual(8);
54235423

54245424
$rootScope.foo = 'foo';
54255425
$rootScope.$digest();
54265426
expect(element.html()).toBe('1:foo;2:;3:foo;4:');
5427-
expect(countWatches($rootScope)).toEqual(4);
5427+
expect(countWatches($rootScope)).toEqual(6);
54285428

54295429
$rootScope.foo = 'baz';
54305430
$rootScope.bar = 'bar';
54315431
$rootScope.$digest();
54325432
expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar');
5433-
expect(countWatches($rootScope)).toEqual(3);
5433+
expect(countWatches($rootScope)).toEqual(5);
54345434

54355435
$rootScope.bar = 'baz';
54365436
$rootScope.$digest();
@@ -5487,18 +5487,18 @@ describe('$compile', function() {
54875487
compile('<div other-tpl-dir param1="::foo" param2="bar"></div>');
54885488
$rootScope.$digest();
54895489
expect(element.html()).toBe('1:;2:;3:;4:');
5490-
expect(countWatches($rootScope)).toEqual(6); // 4 -> template watch group, 2 -> '='
5490+
expect(countWatches($rootScope)).toEqual(8); // 4 -> template watch group, 2 -> '='
54915491

54925492
$rootScope.foo = 'foo';
54935493
$rootScope.$digest();
54945494
expect(element.html()).toBe('1:foo;2:;3:foo;4:');
5495-
expect(countWatches($rootScope)).toEqual(4);
5495+
expect(countWatches($rootScope)).toEqual(6);
54965496

54975497
$rootScope.foo = 'baz';
54985498
$rootScope.bar = 'bar';
54995499
$rootScope.$digest();
55005500
expect(element.html()).toBe('1:foo;2:bar;3:foo;4:bar');
5501-
expect(countWatches($rootScope)).toEqual(3);
5501+
expect(countWatches($rootScope)).toEqual(5);
55025502

55035503
$rootScope.bar = 'baz';
55045504
$rootScope.$digest();
@@ -5771,6 +5771,46 @@ describe('$compile', function() {
57715771
}
57725772
}));
57735773

5774+
it('should work with filtered literal objects within array literals', inject(function() {
5775+
$rootScope.gabName = 'Gabriel';
5776+
$rootScope.tonyName = 'Tony';
5777+
$rootScope.query = '';
5778+
$rootScope.$apply();
5779+
5780+
compile('<div><span my-component reference="[{name: gabName}, {name: tonyName}] | filter:query">');
5781+
5782+
expect(componentScope.reference).toEqual([{name: $rootScope.gabName}, {name: $rootScope.tonyName}]);
5783+
5784+
$rootScope.query = 'Gab';
5785+
$rootScope.$apply();
5786+
5787+
expect(componentScope.reference).toEqual([{name: $rootScope.gabName}]);
5788+
5789+
$rootScope.tonyName = 'Gab';
5790+
$rootScope.$apply();
5791+
5792+
expect(componentScope.reference).toEqual([{name: $rootScope.gabName}, {name: $rootScope.tonyName}]);
5793+
}));
5794+
5795+
it('should work with filtered constant literal objects within array literals (constant)', inject(function() {
5796+
$rootScope.query = '';
5797+
$rootScope.$apply();
5798+
5799+
compile('<div><span my-component reference="[{name: \'Gabriel\'}, {name: \'Toni\'}] | filter:query">');
5800+
5801+
expect(componentScope.reference).toEqual([{name: 'Gabriel'}, {name: 'Toni'}]);
5802+
5803+
$rootScope.query = 'Gab';
5804+
$rootScope.$apply();
5805+
5806+
expect(componentScope.reference).toEqual([{name: 'Gabriel'}]);
5807+
5808+
$rootScope.query = 'i';
5809+
$rootScope.$apply();
5810+
5811+
expect(componentScope.reference).toEqual([{name: 'Gabriel'}, {name: 'Toni'}]);
5812+
}));
5813+
57745814
});
57755815

57765816
});
@@ -5829,8 +5869,48 @@ describe('$compile', function() {
58295869
$rootScope.$apply();
58305870

58315871
expect(componentScope.colref).toEqual([$rootScope.collection[0]]);
5832-
expect(componentScope.colrefAlias).toEqual([$rootScope.collection[0]]);
5833-
expect(componentScope.$colrefAlias).toEqual([$rootScope.collection[0]]);
5872+
expect(componentScope.colrefAlias).toEqual(componentScope.colref);
5873+
expect(componentScope.$colrefAlias).toEqual(componentScope.colref);
5874+
5875+
$rootScope.collection[1].name = 'Gab';
5876+
$rootScope.$apply();
5877+
5878+
expect(componentScope.colref).toEqual($rootScope.collection);
5879+
expect(componentScope.colrefAlias).toEqual(componentScope.colref);
5880+
expect(componentScope.$colrefAlias).toEqual(componentScope.colref);
5881+
}));
5882+
5883+
it('should work with filtered objects within a literal collection', inject(function() {
5884+
$rootScope.gab = {
5885+
name: 'Gabriel',
5886+
value: 18
5887+
};
5888+
$rootScope.tony = {
5889+
name: 'Tony',
5890+
value: 91
5891+
};
5892+
$rootScope.query = '';
5893+
$rootScope.$apply();
5894+
5895+
compile('<div><span my-component colref="[gab, tony] | filter:query" $colref$="[gab, tony] | filter:query">');
5896+
5897+
expect(componentScope.colref).toEqual([$rootScope.gab, $rootScope.tony]);
5898+
expect(componentScope.colrefAlias).toEqual(componentScope.colref);
5899+
expect(componentScope.$colrefAlias).toEqual(componentScope.colref);
5900+
5901+
$rootScope.query = 'Gab';
5902+
$rootScope.$apply();
5903+
5904+
expect(componentScope.colref).toEqual([$rootScope.gab]);
5905+
expect(componentScope.colrefAlias).toEqual(componentScope.colref);
5906+
expect(componentScope.$colrefAlias).toEqual(componentScope.colref);
5907+
5908+
$rootScope.tony.name = 'Gab';
5909+
$rootScope.$apply();
5910+
5911+
expect(componentScope.colref).toEqual([$rootScope.gab, $rootScope.tony]);
5912+
expect(componentScope.colrefAlias).toEqual(componentScope.colref);
5913+
expect(componentScope.$colrefAlias).toEqual(componentScope.colref);
58345914
}));
58355915

58365916
it('should update origin scope when isolate scope changes', inject(function() {
@@ -6187,6 +6267,47 @@ describe('$compile', function() {
61876267
}));
61886268

61896269

6270+
it('should work with filtered literal objects within array literals', inject(function() {
6271+
$rootScope.gabName = 'Gabriel';
6272+
$rootScope.tonyName = 'Tony';
6273+
$rootScope.query = '';
6274+
$rootScope.$apply();
6275+
6276+
compile('<div><span my-component ow-ref="[{name: gabName}, {name: tonyName}] | filter:query">');
6277+
6278+
expect(componentScope.owRef).toEqual([{name: $rootScope.gabName}, {name: $rootScope.tonyName}]);
6279+
6280+
$rootScope.query = 'Gab';
6281+
$rootScope.$apply();
6282+
6283+
expect(componentScope.owRef).toEqual([{name: $rootScope.gabName}]);
6284+
6285+
$rootScope.tonyName = 'Gab';
6286+
$rootScope.$apply();
6287+
6288+
expect(componentScope.owRef).toEqual([{name: $rootScope.gabName}, {name: $rootScope.tonyName}]);
6289+
}));
6290+
6291+
it('should work with filtered constant literal objects within array literals', inject(function() {
6292+
$rootScope.query = '';
6293+
$rootScope.$apply();
6294+
6295+
compile('<div><span my-component ow-ref="[{name: \'Gabriel\'}, {name: \'Toni\'}] | filter:query">');
6296+
6297+
expect(componentScope.owRef).toEqual([{name: 'Gabriel'}, {name: 'Toni'}]);
6298+
6299+
$rootScope.query = 'Gab';
6300+
$rootScope.$apply();
6301+
6302+
expect(componentScope.owRef).toEqual([{name: 'Gabriel'}]);
6303+
6304+
$rootScope.query = 'i';
6305+
$rootScope.$apply();
6306+
6307+
expect(componentScope.owRef).toEqual([{name: 'Gabriel'}, {name: 'Toni'}]);
6308+
}));
6309+
6310+
61906311
describe('literal objects', function() {
61916312
it('should copy parent changes', inject(function() {
61926313
compile('<div><span my-component ow-ref="{name: name}">');

0 commit comments

Comments
 (0)