Skip to content

Commit e154f86

Browse files
committed
feat(ngRepeat): provide support for aliasing filtered repeater results as a scope member
ngRepeat can now alias the snapshot of the list of items evaluated after all filters have been applied as a property on the scope. Prior to this fix, when a filter is applied on a repeater, there is no way to trigger an event when the repeater renders zero results. Closes angular#5919 Closes angular#8046
1 parent 2ad1b4c commit e154f86

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

src/ng/directive/ngRepeat.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@
100100
* For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
101101
* will be associated by item identity in the array.
102102
*
103+
* * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
104+
* intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
105+
* when a filter is active on the repeater, but the filtered result set is empty.
106+
*
107+
* For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
108+
* the items have been processed through the filter.
109+
*
103110
* For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
104111
* `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
105112
* with the corresponding item in the array by identity. Moving the same object in array would move the DOM
@@ -132,9 +139,12 @@
132139
I have {{friends.length}} friends. They are:
133140
<input type="search" ng-model="q" placeholder="filter friends..." />
134141
<ul class="example-animate-container">
135-
<li class="animate-repeat" ng-repeat="friend in friends | filter:q">
142+
<li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
136143
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
137144
</li>
145+
<li class="animate-repeat" ng-if="results.length == 0">
146+
<strong>No results found...</strong>
147+
</li>
138148
</ul>
139149
</div>
140150
</file>
@@ -209,8 +219,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
209219
$$tlb: true,
210220
link: function($scope, $element, $attr, ctrl, $transclude){
211221
var expression = $attr.ngRepeat;
212-
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
213-
trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
222+
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
223+
trackByExp, trackByExpGetter, aliasAs, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
214224
lhs, rhs, valueIdentifier, keyIdentifier,
215225
hashFnLocals = {$id: hashKey};
216226

@@ -221,7 +231,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
221231

222232
lhs = match[1];
223233
rhs = match[2];
224-
trackByExp = match[3];
234+
aliasAs = match[3];
235+
trackByExp = match[4];
225236

226237
if (trackByExp) {
227238
trackByExpGetter = $parse(trackByExp);
@@ -273,6 +284,10 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
273284
nextBlockOrder = [],
274285
elementsToRemove;
275286

287+
if (aliasAs) {
288+
$scope[aliasAs] = collection;
289+
}
290+
276291
var updateScope = function(scope, index) {
277292
scope[valueIdentifier] = value;
278293
if (keyIdentifier) scope[keyIdentifier] = key;

test/ng/directive/ngRepeatSpec.js

+52
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,58 @@ describe('ngRepeat', function() {
374374
});
375375
});
376376

377+
describe('alias as', function() {
378+
it('should assigned the filtered to the target scope property if an alias is provided', function() {
379+
element = $compile(
380+
'<div ng-repeat="item in items | filter:x as results track by $index">{{item.name}}/</div>')(scope);
381+
382+
scope.items = [
383+
{ name : 'red' },
384+
{ name : 'blue' },
385+
{ name : 'green' },
386+
{ name : 'black' },
387+
{ name : 'orange' },
388+
{ name : 'blonde' }
389+
];
390+
391+
expect(scope.results).toBeUndefined();
392+
scope.$digest();
393+
394+
scope.x = 'bl';
395+
scope.$digest();
396+
397+
expect(scope.results).toEqual([
398+
{ name : 'blue' },
399+
{ name : 'black' },
400+
{ name : 'blonde' }
401+
]);
402+
403+
scope.items = [];
404+
scope.$digest();
405+
406+
expect(scope.results).toEqual([]);
407+
});
408+
409+
it('should render a message when the repeat list is empty', function() {
410+
element = $compile(
411+
'<div>' +
412+
' <div ng-repeat="item in items | filter:x as results">{{item}}</div>' +
413+
' <div ng-if="results.length == 0">' +
414+
' No results found...' +
415+
' </div>' +
416+
'</div>')(scope);
417+
418+
scope.items = [1,2,3,4,5,6];
419+
scope.$digest();
420+
expect(trim(element.text())).toEqual('123456');
421+
422+
scope.x = '0';
423+
scope.$digest();
424+
425+
expect(trim(element.text())).toEqual('No results found...');
426+
});
427+
});
428+
377429

378430
it('should allow expressions over multiple lines', function() {
379431
element = $compile(

0 commit comments

Comments
 (0)