You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
Repository navigation
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
ngRepeat should automatically or optionally render a fallback element when the expression yields no results #5919
Say you have a complex expression on ngRepeat like:
<divng-repeat="item in getItems() | filter:values">...</div>
So how can you detect if there are no items returned after the filter or from the getItems() call without using a scope variable/method? You would have to move everything into a controller.
Despite the repeat expression being complex and a potential bottleneck for an application, extracting the calculation out into a controller and manually watching the data set, filtering it and aggregating it is a lot of extra code after having just created a nice filtering system for your ngRepeat code.
What would be nice is if a fallback DIV was created only when the total (final) results extracted from the ngRepeat expression equal 0. This way you can do something like.
<divng-repeat="item in getItems() | filter:values"><divng-repeat-empty>
There are no results found
</div></div>
Nice, I really like this idea and ng-repeat-empty seems pretty elegant. I can't come up with any other syntaxes but I'd also like to see some alternatives. I assume it would work with .ng-enter/.ng-leave as well?
👍 this is something I constantly have to deal with with the current workaround and having it baked into Angular seems appropriate given how common the use case is.
@eternalmatt the sibling element solution can't be done. Anything that requires an element to be positioned close by another element is too strict. Otherwise you would have to pass the same ngRepeat expression into the HTML attribute.
I like the last example, but it forces you to make an external template. The 2nd example is the best of both worlds, but it also requires strict positioning.
Why would the middle option require strict positioning? It would basically be transclude + filter out elements which don't contain the ng-repeat-empty attribute.
But the third example would be the easiest to implement, and maybe that's a good starting point?
By strict I meant that the <div ng-repeat-empty> must be inside of the repeater. But that's no different than ng-switch. The last example can be done without breaking anything and is easier to read. Let's do that.
With the sibling element example, I was thinking this was feasible since
ngRepeatStart and ngRepeatEnd both exist.
On Feb 3, 2014 5:32 PM, "Matias Niemelä" notifications@github.com wrote:
By strict I meant that the
must be inside of the repeater. But that's no different than ng-switch.
The last example can be done without breaking anything and is easier to
read. Let's do that.
Reply to this email directly or view it on GitHubhttps://github.com//issues/5919#issuecomment-34008554
.
<div ng-repeat="item in getItems() | filter:values">
<div ng-repeat-content>
.. HTML elements go here
</div>
<div ng-repeat-empty="'some-template.html'"></div>
</div>
Also can ng-repeat possibly add variable called empty and then it can be used like:
@matsko
Keep in mind that ng-repeat repeats the element it's on, not its content.
So given:
<ul><ling-repeat="item in list">{{ item }}</li></ul>
using your strictng-repeat-empty would result in invalid html:
<ul><ling-repeat="item in list">
{{ item }}
<ling-repeat-empty>No items</li></li></ul>
Same thing with <tr>, <td>, <option>, etc.
So having the ng-repeat-empty inside the current ng-repeat would cause DOM parsing problems.
What @mb-dev proposes would solve this, but it does create new issues to handle (ng-repeat would need to figure out that a -content is in use, or if one uses -empty without -content)
We can always create a new repeater directive
<ulng-repeat-content="item in list"><li>{{ item }}</li><ling-repeat-empty>No items</li></ul>
I'm sure we can figure out a way to have both ng-repeat and ng-repeat-content share the same repeat-logic code, but different configuration for what to repeat and where.
The difficulty with these strategies is, we want the ng-repeat-empty directive to remove itself from the transcluded content, and give ngRepeat the transclude function.
This doesn't really work with the current compiler design, and it would need to be hacked to support that. The simplest way to make this work would be with just an empty-template-url attribute, because this wouldn't depend on any changes to the compiler.
It's not the prettiest solution of the bunch, but all that's needed is one global isEmpty function to check the newly created scope variable. And that function most likely exists on scope in one way or another in a large app anyways. No extracting complex calculations into a controller, no extra directives/attributes or any of that.
I suggest the following syntax, which is both backward-compatible, does not require giving a specific name to each instance of ng-repeat-empty, and I believe does not break the DOM parser in any way:
<ulng-repeat-container><ling-repeat="item in list">{{item.text}}</li><ling-repeat-empty><spanclass="error">No items found.</span></li></ul>
This syntax can also co-exists with the syntax mentioned by @matsko above.
@IgorMinar any comments on the various syntax options suggested so far?
<div ng-repeat="item in getItems() as repeated_items">...</div>
then you can have ngIf to display text for the zero case and you can also use the alias inside of the repeater to display stuff like "1 of 3" when the getItems returns a collection with a length that you don't otherwise know.
There's already a solution for this: you can use the alias expression as <something>, which will publish <something> into the scope, and then you can check if <something> has no length, and display something in that case.
This is in the docs: https://code.angularjs.org/1.6.8/docs/api/ng/directive/ngRepeat
See the example.
Activity
IgorMinar commentedon Jan 21, 2014
Can anyone think of a different syntax that we should consider?
I'd like to compare several syntaxes before we settle on the final api.
scottopherson commentedon Jan 21, 2014
Nice, I really like this idea and
ng-repeat-emptyseems pretty elegant. I can't come up with any other syntaxes but I'd also like to see some alternatives. I assume it would work with.ng-enter/.ng-leaveas well?matsko commentedon Jan 22, 2014
@scottopherson yup.
Micka33 commentedon Jan 22, 2014
Like it. That would be nice to be able to set the
ng-repeat-emptytemplate using a URL as well.scottopherson commentedon Jan 22, 2014
@Micka33 I suppose
<div ng-repeat-empty ng-include="'some-template.html'"></div>would work.benjamingr commentedon Jan 31, 2014
👍 this is something I constantly have to deal with with the current workaround and having it baked into Angular seems appropriate given how common the use case is.
eternalmatt commentedon Feb 3, 2014
Great idea. Proposing two other syntaxes for perspective
ng-repeat-emptya value is equivalent to theng-includefooInlining above syntax into
ng-repeatshould work as well.matsko commentedon Feb 3, 2014
@eternalmatt the sibling element solution can't be done. Anything that requires an element to be positioned close by another element is too strict. Otherwise you would have to pass the same ngRepeat expression into the HTML attribute.
I like the last example, but it forces you to make an external template. The 2nd example is the best of both worlds, but it also requires strict positioning.
caitp commentedon Feb 3, 2014
Why would the middle option require strict positioning? It would basically be transclude + filter out elements which don't contain the ng-repeat-empty attribute.
But the third example would be the easiest to implement, and maybe that's a good starting point?
matsko commentedon Feb 3, 2014
By strict I meant that the
<div ng-repeat-empty>must be inside of the repeater. But that's no different than ng-switch. The last example can be done without breaking anything and is easier to read. Let's do that.eternalmatt commentedon Feb 3, 2014
With the sibling element example, I was thinking this was feasible since
ngRepeatStart and ngRepeatEnd both exist.
On Feb 3, 2014 5:32 PM, "Matias Niemelä" notifications@github.com wrote:
mb-dev commentedon Feb 4, 2014
Syntax similar to ng-switch is also an option:
Also can ng-repeat possibly add variable called empty and then it can be used like:
rodyhaddad commentedon Feb 6, 2014
@matsko
Keep in mind that
ng-repeatrepeats the element it's on, not its content.So given:
using your strict
ng-repeat-emptywould result in invalid html:Same thing with
<tr>,<td>,<option>, etc.So having the
ng-repeat-emptyinside the currentng-repeatwould cause DOM parsing problems.What @mb-dev proposes would solve this, but it does create new issues to handle (
ng-repeatwould need to figure out that a-contentis in use, or if one uses-emptywithout-content)We can always create a new repeater directive
I'm sure we can figure out a way to have both
ng-repeatandng-repeat-contentshare the same repeat-logic code, but different configuration for what to repeat and where.caitp commentedon Feb 6, 2014
The difficulty with these strategies is, we want the ng-repeat-empty directive to remove itself from the transcluded content, and give ngRepeat the transclude function.
This doesn't really work with the current compiler design, and it would need to be hacked to support that. The simplest way to make this work would be with just an empty-template-url attribute, because this wouldn't depend on any changes to the compiler.
tdakhla commentedon Feb 7, 2014
I totally ripped off someone's Plunkr example just now, but here's sorta what we've been doing to solve this issue:
http://plnkr.co/edit/Ro3VnZdJbdX7yEKtqPOe?p=preview
It's not the prettiest solution of the bunch, but all that's needed is one global isEmpty function to check the newly created scope variable. And that function most likely exists on scope in one way or another in a large app anyways. No extracting complex calculations into a controller, no extra directives/attributes or any of that.
13 remaining items
urish commentedon Jun 10, 2014
@esakal thanks for working on this!
I suggest the following syntax, which is both backward-compatible, does not require giving a specific name to each instance of ng-repeat-empty, and I believe does not break the DOM parser in any way:
This syntax can also co-exists with the syntax mentioned by @matsko above.
@IgorMinar any comments on the various syntax options suggested so far?
rodyhaddad commentedon Jul 1, 2014
How about just:
then you can have ngIf to display text for the zero case and you can also use the alias inside of the repeater to display stuff like "1 of 3" when the getItems returns a collection with a length that you don't otherwise know.
matsko commentedon Jul 1, 2014
@rodyhaddad but that aliased value would be registered on the same scope that getItems() lives on.
I really like this solution since it gives the most flexibility.
How does everyone else feel here about this?
IgorMinar commentedon Jul 2, 2014
+1
feat(ngRepeat): provide support for aliasing filtered repeater result…
feat(ngRepeat): provide support for aliasing filtered repeater result…
feat(ngRepeat): provide support for aliasing filtered repeater result…
danijar commentedon Jul 22, 2014
+1
precz commentedon Jul 29, 2014
+1
joshbowdoin commentedon Jul 29, 2014
+1 ! - was hacking my way around this before (often filtering in controller on demand, so that I would have result of filtered list).
huguangju commentedon Sep 2, 2015
+1
yuyudhan commentedon Jan 7, 2018
+1
Narretz commentedon Jan 8, 2018
There's already a solution for this: you can use the alias expression
as <something>, which will publish<something>into the scope, and then you can check if<something>has no length, and display something in that case.This is in the docs: https://code.angularjs.org/1.6.8/docs/api/ng/directive/ngRepeat
See the example.