181

I need to create a comma-separated list of items:

  <li ng-repeat="friend in friends">
      <b ng-repeat="email in friend.email">{{email}}{{$last ? '' : ', '}}</b>...
  </li>

According to the AngularJS documentation, no control flow statements is allowed in expressions. This is why my {{$last ? '' : ', '}} does not work.

Is there an alternative way to create comma-separated lists?

EDIT 1
is there something simpler than:

<span ng-show="!$last">, </span>
Youssef Bouhjira
  • 1,599
  • 2
  • 22
  • 38
Franck Freiburger
  • 26,310
  • 20
  • 70
  • 95
  • 5
    You can always use CSS to format lists in this way (then you don't need to modify HTML when your boss wants them on separate lines etc) - see http://stackoverflow.com/questions/1517220/how-to-style-unordered-lists-in-css-as-comma-separated-text – Alex Sep 02 '15 at 15:25

9 Answers9

342

You could do it this way:

<b ng-repeat="email in friend.email">{{email}}{{$last ? '' : ', '}}</b>

..But I like Philipp's answer :-)

Andrew Joslin
  • 43,033
  • 21
  • 100
  • 75
  • Shouldn't `($last && '' || ', ')` always yield `', '`? – okm May 15 '13 at 11:02
  • 15
    LIke okm above, I couldn't get angular to properly evaluate $last to true or false within a template. I used this: `{{{true: '', false: ', '}[$last]}}`. This technique is more flexible than using `.join` because it allows the elements in the list to each be members of an array, like: `{{friend.email}}{{{true: '', false: ', '}[$last]}}` – Ryan Marcus May 25 '13 at 20:25
  • 3
    Updated to use ternary operators – Andrew Joslin Oct 17 '13 at 15:05
  • 1
    How can i insert and in the printed array so that my array looks like this : John, Mike and Nil. How to get this format without using directive. – MaTya Jul 29 '14 at 12:12
  • I'm using this approach since it allows me to use a filter on each value in the list. – C Fairweather Feb 03 '16 at 18:58
  • I like this one because it works great for iterating over objects as well...thx for the suggestion! – Billy McCafferty Jul 08 '20 at 14:03
232

Just use Javascript's built-in join(separator) function for arrays:

<li ng-repeat="friend in friends">
  <b>{{friend.email.join(', ')}}</b>...
</li>
Philipp Reichart
  • 20,771
  • 6
  • 58
  • 65
  • 21
    If you want HTML-ish separators it's probably time to a write [directive](http://docs.angularjs.org/guide/directive). You could also put HTML into `join()` and disable HTML escaping, but there's a special place in hell for that ;) – Philipp Reichart Jul 18 '12 at 13:07
  • Nice, I didn't even think about using it this way. – Strawberry Jul 18 '13 at 15:12
  • @DavidKEgghead I beg to differ, http://jsfiddle.net/wuZRA/. How does it not work for you? – Philipp Reichart Jan 28 '14 at 23:14
  • 2
    @PhilippReichart sorry for the delay. Here is an example: http://jsfiddle.net/Sek8F/ -- looks like you are targeting a string where as I am referring to an object. – Ravi Ram Feb 11 '14 at 00:25
101

Also:

angular.module('App.filters', [])
    .filter('joinBy', function () {
        return function (input,delimiter) {
            return (input || []).join(delimiter || ',');
        };
    });

And in template:

{{ itemsArray | joinBy:',' }}
sanusart
  • 1,527
  • 1
  • 11
  • 12
42

.list-comma::before {
  content: ',';
}
.list-comma:first-child::before {
  content: '';
}
<span class="list-comma" ng-repeat="destination in destinations">
                            {{destination.name}}
                        </span>
simbu
  • 421
  • 4
  • 2
10

You can use CSS to fix it too

<div class="some-container">
[ <span ng-repeat="something in somethings">{{something}}<span class="list-comma">, </span></span> ]
</div>

.some-container span:last-child .list-comma{
    display: none;
}

But Andy Joslin's answer is best

Edit: I changed my mind I had to do this recently and I ended up going with a join filter.

Chris Stephens
  • 2,164
  • 2
  • 17
  • 21
5

I think it's better to use ng-if. ng-show creates an element in the dom and sets it's display:none. The more dom elements you have the more resource hungry your app becomes, and on devices with lower resources the less dom elements the better.

TBH <span ng-if="!$last">, </span> seems like a great way to do it. It's simple.

the7erm
  • 215
  • 2
  • 9
  • I like this approach, as it's simple and still supports html based separators. Thanks @the7erm! – alexkb Mar 03 '16 at 01:09
3

Since this question is quite old and AngularJS had had time to evolve since then, this can now be easily achieved using:

<li ng-repeat="record in records" ng-bind="record + ($last ? '' : ', ')"></li>.

Note that I'm using ngBind instead of interpolation {{ }} as it's much more performant: ngBind will only run when the passed value does actually change. The brackets {{ }}, on the other hand, will be dirty checked and refreshed in every $digest, even if it's not necessary. Source: here, here and here.

angular
  .module('myApp', [])
  .controller('MyCtrl', ['$scope',
    function($scope) {
      $scope.records = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    }
  ]);
li {
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
  <ul>
    <li ng-repeat="record in records" ng-bind="record + ($last ? '' : ', ')"></li>
  </ul>
</div>

On a final note, all of the solutions here work and are valid to this day. I'm really found to those which involve CSS as this is more of a presentation issue.

Community
  • 1
  • 1
Cosmin Ababei
  • 7,003
  • 2
  • 20
  • 34
1

I like simbu's approach, but I ain't comfortable to use first-child or last-child. Instead I only modify the content of a repeating list-comma class.

.list-comma + .list-comma::before {
    content: ', ';
}
<span class="list-comma" ng-repeat="destination in destinations">
    {{destination.name}}
</span>
Durrahan
  • 25
  • 4
0

If you are using ng-show to limit the values, the {{$last ? '' : ', '}} won`t work since it will still take into consideration all the values.Example

<div ng-repeat="x in records" ng-show="x.email == 1">{{x}}{{$last ? '' : ', '}}</div>

var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope) {
  $scope.records = [
    {"email": "1"},
    {"email": "1"},
    {"email": "2"},
    {"email": "3"}
  ]
});

Results in adding a comma after the "last" value,since with ng-show it still takes into consideration all 4 values

{"email":"1"},
{"email":"1"},

One solution is to add a filter directly into ng-repeat

<div ng-repeat="x in records | filter: { email : '1' } ">{{x}}{{$last ? '' : ', '}}</div>

Results

{"email":"1"},
{"email":"1"}
Mihai
  • 26,325
  • 7
  • 66
  • 81
  • Any suggestion on how to solve this issue the same way in Angular 2 without creating a custom pipe? – chaenu Oct 17 '17 at 12:57