19

I'm trying to use ng-repeat on a div which should contain a star image, each pie in the JSON has a rating property from 1-5, and I want to use this value to loop out x number of stars. I've got this working somewhat but it's flawed in the way that I can't re-sort the array and make the stars follow the correct item in the list since I'm using [$index] to track the iteration.

My solution is rather ugly as well since I'm creating arrays with as many index placeholders as the value of the rating property, and then pushing this into an array to loop out the appropriate number of images. I would like to have a more elegant solution.

How should I go about this problem without using [$index]?

Snippet of the JSON:

{"pies": [
    ...

    {
        "name": "Blueberry pie", 
        "imageUrl": "img/blueberrypie.png", 
        "id": "1",
        "rating": "5", //Ng-repeat depending on this value
        "description": "Blueberry pie is amazing."
    },

    ...
]}

My controller:

pieShopApp.controller('shopCtrl', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
    $scope.pieId = $routeParams.pieId,
    $scope.sortingOptions = ['A-Z', 'Rating'],
    $scope.sortingValues = ['name', 'rating'],
    $scope.ratings = [],
    $http.get('jsons/pies.json')
         .success(function(data, status) {
            $scope.pies = data;

            for (i = 0; i < $scope.pies.pies.length; i++) {

                switch ($scope.pies.pies[i].rating) {

                    case "1": $scope.ratings.push(["1"]); break;

                    case "2": $scope.ratings.push(["1", "2"]); break;

                    case "3": $scope.ratings.push(["1", "2", "3"]); break;

                    case "4": $scope.ratings.push(["1", "2", "3", "4"]); break;

                    case "5": $scope.ratings.push(["1", "2", "3", "4", "5"]); break;
                }
            }
            console.log($scope.ratings);
         })
         .error(function(status) {
            console.log(status);
         })
}]);

The list which contains the pie items:

<div id="pie-list-wrapper">
    <ul class="nav">
        <a href="#/pies/pieid" ng-repeat="pie in pies.pies | filter:query | orderBy:orderProp">
            <li class="list-item rounded-corners box-shadow">
                <aside>
                    <img src="{{pie.imageUrl}}" no-repeat alt="Image of the pie">
                </aside>
                <header>
                    <h1 ng-bind="pie.name" id="item-name" class="bold-text"></h1>
                </header>
                <article>
                    <span ng-bind="pie.description" id="item-desc"></span>
                </article>
                <footer id="item-rating">
                    <div ng-repeat="rating in ratings[$index]" class="rating-box"></div> //Contains the stars
                </footer>
            </li>
        </a>
    </ul>
</div>

Outcome:

pies list

dhavalcengg
  • 4,678
  • 1
  • 17
  • 25
Chrillewoodz
  • 27,055
  • 21
  • 92
  • 175

5 Answers5

19

Checkout this

<div ng-app='myApp' ng-controller="Main">
  <span ng-repeat="n in range('5')">Start{{$index}} &nbsp;&nbsp;</span>
</div>

$scope.range = function(count){

  var ratings = []; 

  for (var i = 0; i < count; i++) { 
    ratings.push(i) 
  } 

  return ratings;
}

Change your html to following

<div id="pie-list-wrapper">
  <ul class="nav">
    <a href="#/pies/pieid" ng-repeat="pie in pies.pies | filter:query | orderBy:orderProp">
      <li class="list-item rounded-corners box-shadow">
        <aside>
          <img src="{{pie.imageUrl}}" no-repeat alt="Image of the pie">
        </aside>
        <header>
          <h1 ng-bind="pie.name" id="item-name" class="bold-text"></h1>
        </header>
        <article>
          <span ng-bind="pie.description" id="item-desc"></span>
        </article>
        <footer id="item-rating">
          <div ng-repeat="start in range(pie.rating)" class="rating-box"></div> //Contains the stars
        </footer>
      </li>
    </a>
  </ul>
</div>
Chrillewoodz
  • 27,055
  • 21
  • 92
  • 175
dhavalcengg
  • 4,678
  • 1
  • 17
  • 25
3

I solved this in this way: "items" is your array of objects in the $scope, accesing the property "rating", you can show the star if the value is less or the same comparing to the "rating" property.

In this example I'm using some icon fonts but for the case of an image is the same thing.

<div ng-repeat="item in items">
    <div class="item-offers"">
        <img ng-src="{{item.image}}">
        <div class="item-not-rating">
            <i class="icon ion-ios-star icon-rating" ng-if="item.rate >= 1"></i>
            <i class="icon ion-ios-star icon-rating" ng-if="item.rate >= 2"></i>
            <i class="icon ion-ios-star icon-rating" ng-if="item.rate >= 3"></i>
            <i class="icon ion-ios-star icon-rating" ng-if="item.rate >= 4"></i>
            <i class="icon ion-ios-star icon-rating" ng-if="item.rate >= 5"></i>
        </div>                        
    </div>
</div>

I've found a better solution that solves this requirement at all:

https://github.com/fraserxu/ionic-rating

2

It looks like you are iterating on the pies and that's where the $index gets its value from. Instead of ng-repeat="rating in ratings[$index]" you should use ng-repeat="rating in range(pie.rating)" This way, the rating would follow your pie when ordering. Then you could completely remove the loop in the controller.

Could you provide just a bit more HTML so that we could see where the $index comes from?

Regards, Camusensei

EDIT: You are indeed iterating over pies.pies in ng-repeat="pie in pies.pies | filter:query | orderBy:orderProp" So what I wrote earlier should work. See below for exhaustive changes.

Controller:

$http.get('jsons/pies.json')
     .success(function(data, status) {
        $scope.pies = data;
     })
     .error(function(status) {
        console.log(status);
     })

HTML:

<div ng-repeat="rating in range(pie.rating)" class="rating-box"></div>

EDIT2: Sorry, I forgot the range function (inspired from Ariya Hidayat):

$scope.range = function(count){
    return Array.apply(0, Array(+count));
}
Community
  • 1
  • 1
Camusensei
  • 1,475
  • 12
  • 20
1

The problem is that ng-repeat only works with arrays or objects, so you can't say iterate x times (while x is a number)

A solution could be to write a function in JavaScript:

$scope.getNumber = function(num) {
    return (new Array(num));
}

An then use this html to show the stars without $index:

<div ng-repeat="rating in getNumber(pie.rating)"></div> 
Ba5t14n
  • 719
  • 2
  • 5
  • 20
0

In Angular 1.4+ you get the following error when using an empty array:

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed.

The following works:

$scope.range = function(count) {
    return Array.apply(0, Array(+count)).map(function(value,index){
        return index;
    });
}

<div ng-app='myApp' ng-controller="Main">
    <span ng-repeat="n in range(5)">Start{{$index}} &nbsp;&nbsp;</span>
</div>
will.ogden
  • 176
  • 4