Skip to content

Commit b14b6a6

Browse files
committed
feat($compile): add one-way collection bindings
Closes angular#14039
1 parent fb2d3fa commit b14b6a6

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

src/ng/compile.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,11 @@
339339
* One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
340340
* back to the parent. However, it does not make this completely impossible.
341341
*
342+
* By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
343+
* method is used for tracking changes, and the equality check is based on object identity.
344+
* It's also possible to watch the evaluated value shallowly with
345+
* {@link ng.$rootScope.Scope#$watchCollection `$watchCollection`}: use `<*` or `<*attr`
346+
*
342347
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
343348
* no `attr` name is specified then the attribute name is assumed to be the same as the local name.
344349
* Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
@@ -1068,7 +1073,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10681073
var bindingCache = createMap();
10691074

10701075
function parseIsolateBindings(scope, directiveName, isController) {
1071-
var LOCAL_REGEXP = /^([@&<]|=(\*?))(\??)\s*([\w$]*)$/;
1076+
var LOCAL_REGEXP = /^([@&]|[=<](\*?))(\??)\s*([\w$]*)$/;
10721077

10731078
var bindings = createMap();
10741079

@@ -3626,7 +3631,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
36263631
var initialValue = destination[scopeName] = parentGet(scope);
36273632
initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
36283633

3629-
removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
3634+
removeWatch = scope[definition.collection ? "$watchCollection" : "$watch"](parentGet, function parentValueWatchAction(newValue, oldValue) {
36303635
if (oldValue === newValue) {
36313636
if (oldValue === initialValue || (isLiteral && equals(oldValue, initialValue))) {
36323637
return;

test/ng/compileSpec.js

+58
Original file line numberDiff line numberDiff line change
@@ -5148,6 +5148,9 @@ describe('$compile', function() {
51485148
owOptref: '<?',
51495149
owOptrefAlias: '<? owOptref',
51505150
$owOptrefAlias: '<? $owOptref$',
5151+
owColref: '<*',
5152+
owColrefAlias: '<* owColref',
5153+
$owColrefAlias: '<* $owColref$',
51515154
expr: '&',
51525155
optExpr: '&?',
51535156
exprAlias: '&expr',
@@ -6327,6 +6330,61 @@ describe('$compile', function() {
63276330
});
63286331
});
63296332

6333+
describe('one-way collection bindings', function() {
6334+
it('should update isolate scope when origin scope changes', inject(function() {
6335+
$rootScope.collection = [{
6336+
name: 'Gabriel',
6337+
value: 18
6338+
}, {
6339+
name: 'Tony',
6340+
value: 91
6341+
}];
6342+
$rootScope.query = '';
6343+
$rootScope.$apply();
6344+
6345+
compile('<div><span my-component ow-colref="collection | filter:query" $ow-colref$="collection | filter:query">');
6346+
6347+
expect(componentScope.owColref).toEqual($rootScope.collection);
6348+
expect(componentScope.owColrefAlias).toEqual(componentScope.owColref);
6349+
expect(componentScope.$owColrefAlias).toEqual(componentScope.owColref);
6350+
6351+
$rootScope.query = 'Gab';
6352+
$rootScope.$apply();
6353+
6354+
expect(componentScope.owColref).toEqual([$rootScope.collection[0]]);
6355+
expect(componentScope.owColrefAlias).toEqual([$rootScope.collection[0]]);
6356+
expect(componentScope.$owColrefAlias).toEqual([$rootScope.collection[0]]);
6357+
}));
6358+
6359+
it('should update isolate scope when origin literal object content changes', inject(function() {
6360+
$rootScope.gab = {
6361+
name: 'Gabriel',
6362+
value: 18
6363+
};
6364+
$rootScope.tony = {
6365+
name: 'Tony',
6366+
value: 91
6367+
};
6368+
$rootScope.$apply();
6369+
6370+
compile('<div><span my-component ow-colref="[gab, tony]" $ow-colref$="[gab, tony]">');
6371+
6372+
expect(componentScope.owColref).toEqual([$rootScope.gab, $rootScope.tony]);
6373+
expect(componentScope.owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]);
6374+
expect(componentScope.$owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]);
6375+
6376+
$rootScope.tony = {
6377+
name: 'Bob',
6378+
value: 42
6379+
};
6380+
$rootScope.$apply();
6381+
6382+
expect(componentScope.owColref).toEqual([$rootScope.gab, $rootScope.tony]);
6383+
expect(componentScope.owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]);
6384+
expect(componentScope.$owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]);
6385+
}));
6386+
});
6387+
63306388
describe('executable expression', function() {
63316389
it('should allow expression execution with locals', inject(function() {
63326390
compile('<div><span my-component expr="count = count + offset" $expr$="count = count + offset">');

0 commit comments

Comments
 (0)