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

Commit b10b300

Browse files
committed
fix(ngRepeat): support complex assignable aliasAs expressions
Parse aliasAs as an expression, and assert that the expression is assignable. BREAKING CHANGE Previously, any name passed as an expression would make up a single property name, including constant values such as 1, NaN, null, undefined, or even expressions such as function calls or boolean expressions. Now, more complex expressions are possible, allowing the collection alias to be assigned as a property of an object --- however, if the expression is not determined to be assignable, it will throw. Fixes #8438
1 parent e982581 commit b10b300

File tree

3 files changed

+98
-2
lines changed

3 files changed

+98
-2
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@ngdoc error
2+
@name ngRepeat:nonassign
3+
@fullName Non-assignable Expression
4+
@description
5+
6+
Occurs when there is a syntax error in an {@link ng.directive:ngRepeat ngRepeat} expression which is expected to be assignable.
7+
8+
The {@link ng.directive:ngRepeat ngRepeat} directive's `alias as` syntax is used to assign an alias for the processed collection in scope.
9+
10+
If the expression is not assignable (such as a literal value or a function call or comparison operation), the expression is unassignable.
11+
12+
Invalid expressions might look like this:
13+
14+
```html
15+
<li ng-repeat="item in items | filter:searchString as 'some constant value'">{{item}}</li>
16+
<li ng-repeat="item in items | filter:searchString as $collection = []">{{item}}</li>
17+
<li ng-repeat="item in items | filter:searchString as resultOfSomeMethod()">{{item}}</li>
18+
<li ng-repeat="item in items | filter:searchString as null">{{item}}</li>
19+
```
20+
21+
Valid expressions might look like this:
22+
23+
```html
24+
<li ng-repeat="item in items | filter:searchString as collections[0]">{{item}}</li>
25+
<li ng-repeat="item in items | filter:searchString as clothes.tshirts">{{item}}</li>
26+
<li ng-repeat="item in items | filter:searchString as filteredCollection">{{item}}</li>
27+
```

src/ng/directive/ngRepeat.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,13 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
232232

233233
lhs = match[1];
234234
rhs = match[2];
235-
aliasAs = match[3];
235+
aliasAs = match[3] && $parse(match[3]);
236236
trackByExp = match[4];
237237

238+
if (aliasAs && !aliasAs.assign) {
239+
throw ngRepeatMinErr('nonassign', "Expected collection alias to be an assignable expression but got '{0}'.", match[3]);
240+
}
241+
238242
if (trackByExp) {
239243
trackByExpGetter = $parse(trackByExp);
240244
trackByIdExpFn = function(key, value, index) {
@@ -286,7 +290,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
286290
elementsToRemove;
287291

288292
if (aliasAs) {
289-
$scope[aliasAs] = collection;
293+
aliasAs.assign($scope, collection);
290294
}
291295

292296
var updateScope = function(scope, index) {

test/ng/directive/ngRepeatSpec.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,71 @@ describe('ngRepeat', function() {
424424

425425
expect(trim(element.text())).toEqual('No results found...');
426426
});
427+
428+
429+
it('should support complex assignable expressions', function() {
430+
element = $compile(
431+
'<div>' +
432+
' <div ng-repeat="item in items | filter:x as ngRepeat.results track by $index">{{item}}</div>' +
433+
'</div>')(scope);
434+
435+
scope.x = 'bl';
436+
scope.items = [
437+
{ name : 'red' },
438+
{ name : 'blue' },
439+
{ name : 'green' },
440+
{ name : 'black' },
441+
{ name : 'orange' },
442+
{ name : 'blonde' }
443+
];
444+
scope.$digest();
445+
expect(scope.ngRepeat).toEqual({
446+
results: [
447+
{ name : 'blue' },
448+
{ name : 'black' },
449+
{ name : 'blonde' }
450+
]
451+
});
452+
});
453+
454+
455+
it('should throw if expression is not assignable', inject(function($exceptionHandler) {
456+
element = $compile(
457+
'<div>' +
458+
' <div ng-repeat="item in items | filter:x as null track by $index">{{item}}</div>' +
459+
'</div>')(scope);
460+
461+
scope.x = 'bl';
462+
scope.items = [
463+
{ name : 'red' },
464+
{ name : 'blue' },
465+
{ name : 'green' },
466+
{ name : 'black' },
467+
{ name : 'orange' },
468+
{ name : 'blonde' }
469+
];
470+
471+
expect($exceptionHandler.errors.shift()[0].message).
472+
toMatch(/^\[ngRepeat:nonassign\] Expected collection alias to be an assignable expression but got \'null\'\./);
473+
}));
474+
475+
476+
it('should throw if alias as expression is an assignment expression', inject(function($exceptionHandler) {
477+
element = $compile(
478+
'<div>' +
479+
' <div ng-repeat="item in items | filter:x as foo=6 track by $index">{{item}}</div>' +
480+
'</div>')(scope);
481+
expect($exceptionHandler.errors.shift()[0].message).
482+
toMatch(/^\[ngRepeat:nonassign\] Expected collection alias to be an assignable expression but got \'foo=6\'\./);
483+
dealoc(element);
484+
485+
element = $compile(
486+
'<div>' +
487+
' <div ng-repeat="item in items | filter:x as foo = 6 track by $index">{{item}}</div>' +
488+
'</div>')(scope);
489+
expect($exceptionHandler.errors.shift()[0].message).
490+
toMatch(/^\[ngRepeat:nonassign\] Expected collection alias to be an assignable expression but got \'foo = 6\'\./);
491+
}));
427492
});
428493

429494

0 commit comments

Comments
 (0)