-
Notifications
You must be signed in to change notification settings - Fork 27.4k
ngRepeat should automatically or optionally render a fallback element when the expression yields no results #5919
Comments
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. |
Nice, I really like this idea and |
@scottopherson yup. |
Like it. That would be nice to be able to set the |
@Micka33 I suppose |
👍 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. |
Great idea. Proposing two other syntaxes for perspective
Inlining above syntax into
|
@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 |
With the sibling element example, I was thinking this was feasible since
|
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:
|
@matsko <ul>
<li ng-repeat="item in list">{{ item }}</li>
</ul> using your strict <ul>
<li ng-repeat="item in list">
{{ item }}
<li ng-repeat-empty>No items</li>
</li>
</ul> Same thing with What @mb-dev proposes would solve this, but it does create new issues to handle ( We can always create a new repeater directive <ul ng-repeat-content="item in list">
<li>{{ item }}</li>
<li ng-repeat-empty>No items</li>
</ul> I'm sure we can figure out a way to have both |
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. |
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. |
personally, I don't like the idea of the extra directive at the same level as the as the ng-repeat. That feels like coupling to me... I think the framework provides the ability to solve this well already as demostrated in tdakhla's example so I would prefer not seeing anything new for this use case. |
Thinking about this more, I don't like placing in an extra item into the repeat element. It can easily be regarded as repeated content, even though it will get cut out of the repeater upon compile. I like the idea of a local variable that is a snapshot of the current collection. <div ng-switch="repeated_items.length" ng-if="repeated_items">
<div ng-switch-when="0">There are no items</div>
<div ng-switch-when="1">There is currently only one item</div>
<div ng-switch-default>There are {{ repeated_items.length }} items</div>
</div>
<div ng-repeat="item in getItems()" ng-repeat-alias="repeated_items">
{{ item }}
</div> This way ngRepeat doesn't have to be changed much and it won't break anything. The only problem with the example above is that the ngSwitch will only render itself after the 2nd digest. What do you guys think? |
If possible, we could also handling the alias as: <div ng-repeat="item in getItems() alias as repeated_items">...</div> When a filter or track by is added, it would appear at the end: <div ng-repeat="item in getItems() | filter:q alias as repeated_items">...</div>
<div ng-repeat="item in getItems() track by item.id alias as repeated_items">...</div>
<div ng-repeat="item in getItems() | filter:q track by item.id alias as repeated_items">...</div> |
This has been suggested earlier on this thread
I will add my thoughts. What if the value of
or
|
+1 |
We're going with |
+1. Any updates? |
I worked on this issue while participating in an hackathon in ng-conf Israel, http://ng-conf.gdg.co.il/ I'm adding a plunker with my suggested solution http://run.plnkr.co/plunks/oDbNNg/ I support setting the empty directive on a parent element of the repeat directive to allow using a different type of element when no items found as shown in sample 2 Sample 1 - Showing fallback for repeat as li element <ul data-ng-repeat-empty="noItemsFound">
<li ng-repeat="item in list3 | idValueLowerThen100">{{item.text}}</li>
<li name="noItemsFound"><span style="color:red">No Items Found </span></li>
</ul> Sample 2 - Showing fallback for repeat as sibiling div of the ul (external to ul) <div data-ng-repeat-empty="noItemsFound">
<ul>
<li ng-repeat="item in list2 | idValueLowerThen100">{{item.text}}</li>
</ul>
<div name="noItemsFound">
<div style="color:red">No Items Found </div>
</div>
</div> I also pushed my changes to my repository https://github.com/esakal/angular.js/commit/ea638a685227c1ef109b75d07216e0fa813594bf Any comments, suggestions will be appriciated |
@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: <ul ng-repeat-container>
<li ng-repeat="item in list">{{item.text}}</li>
<li ng-repeat-empty><span class="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? |
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. |
@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? |
+1 |
…s 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
…s 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
…s 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 |
1 similar comment
+1 |
+1 ! - was hacking my way around this before (often filtering in controller on demand, so that I would have result of filtered list). |
+1 |
1 similar comment
+1 |
There's already a solution for this: you can use the alias expression |
Say you have a complex expression on ngRepeat like:
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.
The text was updated successfully, but these errors were encountered: