43

I have a list of items ...

$scope.Users =[{
    UserName: ''
}];

In my view I want to list them like this assuming I have only 4 items in my $scope:Users

Username1, Username2, Username3 and Username4

<span data-ng-repeat="user in Users">{{user.Username}}</span>{{$last ? '' : ', '}}

The above expression will basically add comma after each item and works fine.

My problem is how do I add an and keyword before the last item so it will be like:

Username1, Username2, Username3 and Username4

instead of:

Username1, Username2, Username3, Username4
Ari
  • 460
  • 6
  • 13
David Dury
  • 5,537
  • 12
  • 56
  • 94

6 Answers6

58

$last is the truthy value.. so it holds either true or false and it doesn't hold the last element index..

I guess below expression should solve your problem

<p><span ng-repeat="user in Users">
            {{user.Username}} {{$last ? '' : ($index==Users.length-2) ? ' and ' : ', '}}
  </span></p>

Also make sure that you have the expression with $last within ng-repeat element and not outside of it

Please check the below working fiddle

http://jsfiddle.net/hrishi1183/Sek8F/2/

Hrishi
  • 761
  • 4
  • 12
36

This could be one of the solutions

<span data-ng-repeat="user in Users">{{user.Username}}<font ng-show="!$last">,</font></span>
amarmishra
  • 603
  • 6
  • 15
22
<span ng-repeat="user in Users">{{$first ? '' : $last ? ' and ' : ', '}}{{user.Username}}</span>

Instead of appending something prepend it. You can use $first to omit the first one. You can then use $last to add "and" instead of a comma.

a better oliver
  • 26,330
  • 2
  • 58
  • 66
10

Use a comma before and to promote clarity!

Bill Gates donates 6 million to Username1, Username2 and Username3.

vs

Bill Gates donates 6 million to Username1, Username2, and Username3.

Without the serial comma, the sentence does not clearly indicate that each of the users is to be given an equal share.

<span ng-repeat="user in Users">
     {{user.Username}}{{$last ? '' : ($index==Username.length-2) ? ', and ' : ',&nbsp;'}}
</span>

Outputs:

Username1, Username2, Username3, and Username4
Community
  • 1
  • 1
ToddBallard
  • 181
  • 1
  • 2
  • 5
4

If you just need the text output, use a custom filter instead of ng-repeat:

<span>{{Users | humanizedUserList}}</span>

the filter code being something like this (using Lodash):

app.filter('humanizedUserList', function() {
  return function(users) {
    var last_users = _.last(users, 2);
    return _.reduce(users, function(out, user, idx, users) {
      out += user.UserName;

      if(user === last_users[1]) { // last entry
        return out;
      }
      else if(user === last_users[0]) { // second to last entry
        return out + ', and ';
      }
      else {
        return out + ', ';
      }
    });
  };
}

You'd save yourself the hackish use of $last outside of the ng-repeat and ternary operators - and add reusability for other parts of your application.

yerforkferchips
  • 1,965
  • 1
  • 19
  • 27
4

I ran into this problem today, but with an extra challenge that the list items may need to be formatted within the sentence (for example, bold items or links). So just outputting a string from a filter wouldn't work. Initially I tried using ng-bind-html and outputting HTML from the filter, but that wasn't flexible enough.

I ended up making a directive that can be used within any ng-repeat to add the separator between list items. The tricky part is I only had access to $first, $last, and $index (not the original array or its length!).

My solution:

var app = angular.module('app', []);
app.directive('listSeparator', function() {
  return {
    replace: true,
    template: '<span>{{ separator }}</span>',
    link: function(scope, elem, attrs) {
      scope.$watchGroup(["$index", "$first", "$last"], function() {
        if (scope.$first)
          scope.separator = "";
        else if (scope.$last && scope.$index == 1)
          scope.separator = " and ";
        else if (scope.$last)
          scope.separator = ", and ";
        else
          scope.separator = ",";
      })
    }
  }
})
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.10/angular.min.js"></script>
<div ng-app="app">
    Click a button to see the formatted list.
    <button ng-click="myArray = ['one']">One item</button>
    <button ng-click="myArray = ['one','two']">Two items</button>
    <button ng-click="myArray = ['one','two','three']">Three items</button><br>
    <span ng-repeat="item in myArray"><list-separator></list-separator>
        <strong>{{item}}</strong></span>

</div>

Enjoy!

heather
  • 224
  • 3
  • 5