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

7
  • maybe stackoverflow.com/questions/16824853/… will help Commented Jan 24, 2015 at 12:21
  • @cyan looked at that one twice already but couldn't figure out how to get it to work in my case. Commented Jan 24, 2015 at 12:22
  • $scope.ratings is filled in the same order as json data?By the way, could you make code in success function make another function for code cleaning? Commented Jan 24, 2015 at 12:26
  • As I understand, you try to list some data while each data element has a star rating from 1 to 5. Is that correct? Commented Jan 24, 2015 at 12:33
  • Yes, look at the image I added. Commented Jan 24, 2015 at 12:37

5 Answers 5

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>
Sign up to request clarification or add additional context in comments.

9 Comments

How do I pass the current rating value as a parameter though?
edited my answer. Now you don't need ratings array. Just pass pie.rating to range function. And you are good to go.
Hmm.. I got a bit of an odd one. When using +count I get no stars at all, if I use count I only get one. Any idea why? My solution is identical to yours.
use other variable then rating in ng-repeat. Updated my answer.
This could happen if your pie.rating is blank string. Can you check pie.rating again.?
|
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

Comments

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));
}

6 Comments

Look at my comment in the answer above.
For debug purposes, use this range function: $scope.range = function(count){var a = new Array(+count); console.log(count, a); return a;};
We solved it, still doesn't know what caused it to malfunction though. I will investigate this matter further.
Okay, I found out where the problem was : 2ality.com/2012/06/dense-arrays.html The array was sparse instead of dense
Interesting, but I tried that solution but didn't work either. It feels like there's something else that's causing the malfunction. At least I've found a solution that works now ;S
|
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> 

Comments

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>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.