Star Ratings in Ionic 2

Hi All,

Can anyone please recommend how to do a Star Ratings implementation in Ionic 2. Are there any plugins or scripts available? I have seen a few, but all have examples for Ionic 1. (For example, can Angular Ratings be used with Ionic 2?. How do you import it?)

  • needs to be able to display half star

Thanks

I would like to see an example component too, because I need it to rate and to display the ratings hopefully exactly as they are (3.8, 4.7, 4.9). Maybe we can port some code from Angular Ratings, nice stuff.

I have found this which shows how to do it in Angular 1 & Angular 2. So close to what we need I think. However, I am pretty new to Ionic, and am finding Angular 2 has some differences with ionic 2 which are confusing me.

For example, doesn’t Ionic 2 not have directives?

directives: [NgFor]

Also, does Ionic 2 use @View?

I’m new to stand alone @Components on Ionic 2 but you can generate them with:

ionic g component rating

the template is configured there, and then port the code there :smiley:
then we can use the new component on any Page of the Ionic 2 app adding the

import { Rating } from '../components/rating'

@Component {
  ...
  directives: [ Rating ]

something like that! gotta try :slight_smile:

Yes, you can declare directives in Ionic components. No, you don’t need to explicitly do it with ngFor: Ionic does it for you. Nobody uses @View any more: it was removed upstream.

Thanks guys, I’ll give it a try

Hi guys,

After I add <rating></rating>:

  <ion-item>
    <p><rating></rating></p>                
  </ion-item>

I get the following error:

EXCEPTION: Error: Uncaught (in promise): Template parse errors:
There is no directive with "exportAs" set to "index" ("
    <span tabindex="0">
      <template ng-for [ng-for-of]="range" [ERROR ->]#index="index">
        <span class="sr-only">(*)</span>
        <i class="glyphicon glyphicon-star">"): Rating@2:43
Can't bind to 'ng-for-of' since it isn't a known native property ("
    <span tabindex="0">
      <template ng-for [ERROR ->][ng-for-of]="range" #index="index">
        <span class="sr-only">(*)</span>
        <i class="glyphi"): Rating@2:23
Property binding ng-for-of not used by any directive on an embedded template ("
    <span tabindex="0">
      [ERROR ->]<template ng-for [ng-for-of]="range" #index="index">
        <span class="sr-only">(*)</span>
       "): Rating@2:6

Does that mean I to declare ngFor as a directive? How do I do this?

I’m using Ionic 2.0.0-beta.32

Thanks

I’ve had an error like this using some old code on Angular 2, what’s your template code? can you post it?

Thanks matheo

search.html

  <ion-list>
    <ion-item-sliding *ngFor="let item of employeeModels">
      <ion-item>
        {{ formatEmployee(item) }}
        <ion-avatar item-left><img [src]="item.avatar64 ? item.avatar64 : 'images/blank-profile-picture.png'"></ion-avatar>
        <h2>{{item.firstName}}</h2>
        <p>{{item.categories[0].name}}&nbsp;{{item.subCategories[0].name}}&nbsp;{{item.time}}&nbsp;<rating></rating></p>                
      </ion-item>
      <ion-item-options>
        <button primary (click)="alert('todo')"><ion-icon name="text"></ion-icon>Message</button>
        <button light (click)="alert('todo')"><ion-icon class="ion-ios-heart"></ion-icon>Favourite</button>
      </ion-item-options>
    </ion-item-sliding>
  </ion-list>

<ion-infinite-scroll (ionInfinite)=“doInfinite($event)”>

search.ts

import { Component, ChangeDetectionStrategy, enableProdMode, ChangeDetectorRef } from '@angular/core';
import { NavController, Loading } from 'ionic-angular';
import { EmployeeService } from '../service/employeeService';
import { UtilityService } from '../utils/utilityService';
import { Employee } from '../model/employee';
import { Rating } from '../utils/rating/rating';
//enableProdMode();
@Component({
  templateUrl: 'build/pages/search/search.html',
  providers: [EmployeeService, UtilityService],
  changeDetection: ChangeDetectionStrategy.OnPush,
  directives: [Rating]
})
export class SearchPage {
  private MAX_RESULTS: number = 20;
  private firstResult: number = 0;
  private ref: ChangeDetectorRef;
  private nav: NavController = null;
  private loading: Loading = Loading.create();;
  private employeeService: EmployeeService = null;
  private utilityService: UtilityService = null;
  private employeeModel: Employee = null;
  private employeeModels: Employee[] = [];
  constructor(ref: ChangeDetectorRef, nav: NavController, employeeService: EmployeeService, utilityService: UtilityService) {
    this.ref = ref;
    this.nav = nav;
    this.employeeService = employeeService;
    this.utilityService = utilityService;
  }
  ngOnInit() {
    this.getEmployeeRange();
  }
  doInfinite(infiniteScroll: any) {
    this.firstResult += this.MAX_RESULTS;
    this.employeeService.getEmployeeRange(this.firstResult, this.MAX_RESULTS).then((employeeModels: Employee[]) => {
      for (var index = 0; index < employeeModels.length; index++) {
        this.employeeModels.push(employeeModels[index]);
      }
      infiniteScroll.complete();
    });
  }
  getEmployeeRange() {
    this.nav.present(this.loading);
    this.employeeService.getEmployeeRange(this.firstResult, this.MAX_RESULTS).then((employeeModels: Employee[]) => {
      for (var index = 0; index < employeeModels.length; index++) {
        this.employeeModels.push(employeeModels[index]);
      }
      this.loading.dismiss();
      this.ref.markForCheck();
    });
  }

rating.ts

import {Component} from '@angular/core';
@Component({
  selector: 'rating',
  template: `
    <span tabindex="0">
      <template ng-for [ng-for-of]="range" #index="index">
        <span class="sr-only">(*)</span>
        <i class="glyphicon glyphicon-star"></i>
      </template>
    </span>
  `
})
export class Rating {
  private range: Array<number> = [1, 2, 3, 4, 5];
}

Example directive for star rating in angular 2

Thanks amityadav, that Plunker example looks the same as the one I am using here. However, I am having problems converting the Angular 2 conventions to Ionic 2.

To me, the faulty code is:

#index="index"

somehow the range Array<number> is not exporting its index to be handled by the ngFor, maybe changing the ngFor syntax, or search an updated reference of it, because it can be a little old code, that API changed recently.

Thanks matheo, You are right. I changed the code to the following and it works. It displays stars as (*) (*) (*) (*) (*). Which is what is expected according to this.

@Component({
   selector: 'rating',
  template: `
      <span tabindex="0">
          <template ngFor let-val [ngForOf]="range" let-index="index">
              <span class="sr-only">(*)</span>
             <i class="glyphicon glyphicon-star"></i>
          </template>
     </span>
 `,
  directives: []
})
export class Rating {
    private range:Array<number> = [1,2,3,4,5];
}

Awesome news!
Happy coding then, and thanks for this!
It will help me the next week when I have time to get on my rating component too :slight_smile:

Great, thank you for the help. Good luck with your project. I will do some more work on this tomorrow, I will post my code here if I have success so you can make use of it if you like.

Just wanted to share my RatingComponent:

import {Component, ChangeDetectionStrategy, Input} from '@angular/core';
import {Icon} from 'ionic-angular/components/icon/icon';

@Component({
	selector: 'rating',
	directives: [Icon],
	template: `
		<ul>
			<li *ngFor="let icon of icons()">
				<ion-icon [name]="icon"></ion-icon>
			</li>
		</ul>
	`,
	styles: [`
		ul {
			display: inline-block;
			list-style: none;
			padding: 0;
			margin: 0;
		}
		li {
			display: inline-block;
			color: #ffa500;
		}
		li + li {
			margin-left: .1em;
		}
	`],
	changeDetection: ChangeDetectionStrategy.OnPush
})
export class RatingComponent {

	@Input() public score: number = 1;
	@Input() public max: number = 5;

	@Input() public iconEmpty: string = 'star-outline';
	@Input() public iconHalf: string = 'star-half';
	@Input() public iconFull: string = 'star';

	public icons(): string[] {
		let step = 0.5;
		let score = Math.ceil(this.score / step) * step;

		let icons = [];
		for (let i = 1; i <= this.max; i++) {
			if (i <= score) {
				icons.push(this.iconFull);
			} else if (i - step <= score) {
				icons.push(this.iconHalf);
			} else {
				icons.push(this.iconEmpty);
			}
		}
		return icons;
	}

}

Use it like this in templates:

<rating score="4" max="5"></rating>

2 Likes

Hi infoproducts,

Looks great. Nice that it shows half stars too.

Do you have any code to also update the stars? i.e. to select a star rating?

Thanks

No, sorry. Haven’t had the need for it yet, but probably shouldn’t be too hard to add

Thanks infoproducts, I’ll look to do it tomorrow and add the code here.

This works, you need to add an index and an update function.

Thanks for your help matheo & infoproducts. matheohopefully this helps you too.

import {Component, ChangeDetectionStrategy, Input} from '@angular/core';
import {Icon} from 'ionic-angular/components/icon/icon';
@Component({
	selector: 'rating',
	directives: [Icon],
	template: `
		<ul>
			<li *ngFor="let icon of icons(); let i = index" (click)="update(i+1)">
				<ion-icon [name]="icon"></ion-icon>
			</li>
		</ul>
	`,
	styles: [`
		ul {
			display: inline-block;
			list-style: none;
			padding: 0;
			margin: 0;
		}
		li {
			display: inline-block;
			color: #ffa500;
		}
		li + li {
			margin-left: .1em;
		}
	`],
	changeDetection: ChangeDetectionStrategy.OnPush
})
export class RatingComponent {
	@Input() public score: number = 1;
	@Input() public max: number = 5;
	@Input() public iconEmpty: string = 'star-outline';
	@Input() public iconHalf: string = 'star-half';
	@Input() public iconFull: string = 'star';
	public update(index: number) {
		this.score = index;
	}
	public icons(): string[] {
		let step = 0.5;
		let score = Math.ceil(this.score / step) * step;
		let icons = [];
		for (let i = 1; i <= this.max; i++) {
			if (i <= score) {
				icons.push(this.iconFull);
			} else if (i - step <= score) {
				icons.push(this.iconHalf);
			} else {
				icons.push(this.iconEmpty);
			}
		}
		return icons;
	}
}
2 Likes