rateChange on ngb-raring fired while page is on initialization

Viewed 385

I am very new to angular, I am trying to send a new Rate to the dataBase using rateChange in ngb-rating but it have been fired while it gitting the values from the server for example when I try to console.log the Rate it print the rate that I am getting from the DataBase.

this is the Html Templet:

<div class="card-group d-flex  justify-content-center">

    <div class="row justify-content-center">

        <div *ngFor="let item of items" class="col-sm-4  card bg-light"
            style=" max-width: 17rem; border:none; display: flex;margin: 30px;">

            <div class="card-img-top" style="height: 45%; white-space: nowrap;">
                <span class="helper"></span>
                <img src="{{item.image}}" style="margin: 0px; vertical-align: middle; display: inline-block;"
                    width="100%" alt="item.Name">
            </div>
            <div class="card-body">
                <h5 class="card-title" style="text-align: center;">{{item.Name}}</h5>
                <p class="card-description" style="height: 30%;">{{item.Description | Summery}}</p>
                <h5 class="card-text  price" style=" margin: 0px 0px 20px 0px ;">{{item.price | currency}}
                </h5>
                <ngb-rating [max]='5' [(rate)]="item.stars" *ngIf="" (rateChange)="onRateChange($event)">
                    <ng-template let-fill="fill">
                        <span class="star" [class.filled]="fill === 100">&#9733;</span>
                    </ng-template>
                </ngb-rating>
            </div>
            <button class="btn btn-primary mx-auto">add to cart</button>


        </div>
    </div>
</div>

ts file:

import { Component, OnInit } from '@angular/core';
import { Item } from 'src/moduls/item';
import { ItemService } from './item.service';

@Component({
  selector: 'app-item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css']
})

export class ItemComponent implements OnInit {

  constructor(private itemService: ItemService) { }
  items: any[] = [];
  onRateChange(newRate: number) {
    console.log(newRate);
  }

  ngOnInit(): void {
    this.getItems();
  }



  getItems() {
    return this.itemService.getaData().subscribe(
      Response => {
        this.items = Response as Item[];
        console.log(this.items);
      }
    )
  }

}
1 Answers

The ng-bootstrap is actually lying to us. Looking into the rating sources code I see the rateChange is emitted whenever the rating value is changed even if the user is not involved.

I see only one solution - leverage the reactive forms. You might subscribe to the valueChanges observable after the initial value is set.

import { Component, OnInit, OnDescroy } from '@angular/core';
import { Item } from 'src/moduls/item';
import { ItemService } from './item.service';

@Component({
  selector: 'app-item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css']
})

export class ItemComponent implements OnInit, OnDestroy {
  onDestroy$: Subject = new Subject();
  items: Array<{control: FormControl, item: Item}> = [];

  constructor(private itemService: ItemService) { }

  ngOnInit(): void {
    this.getItems();
  }

  ngOnDestroy(): void {
    this.onDestroy$.next();
  }

  getItems() {
    return this.itemService.getaData().subscribe(
      Response => {
        this.items = (Response as Item[]).map(item => {
           const control = new FormControl(item.stars);
           control.valueChanges
              .tap(takeUntil(this.onDestroy$))
              .subscribe(newRate => this.saveRating(newRate,item));
           return {control, item};
        });
        console.log(this.items);
      }
    )
  }

  saveRating(newRate: number, item: Item) {
    console.log(newRate, item);
  }

}
<div class="card-group d-flex  justify-content-center">

    <div class="row justify-content-center">

        <div *ngFor="let item of items" class="col-sm-4  card bg-light"
            style=" max-width: 17rem; border:none; display: flex;margin: 30px;">

            <div class="card-img-top" style="height: 45%; white-space: nowrap;">
                <span class="helper"></span>
                <img src="{{item.image}}" style="margin: 0px; vertical-align: middle; display: inline-block;"
                    width="100%" alt="item.item.Name">
            </div>
            <div class="card-body">
                <h5 class="card-title" style="text-align: center;">{{item.item.Name}}</h5>
                <p class="card-description" style="height: 30%;">{{item.item.Description | Summery}}</p>
                <h5 class="card-text  price" style=" margin: 0px 0px 20px 0px ;">{{item.item.price | currency}}
                </h5>
                <ngb-rating [max]='5' [formControl]='item.control'>
                    <ng-template let-fill="fill">
                        <span class="star" [class.filled]="fill === 100">&#9733;</span>
                    </ng-template>
                </ngb-rating>
            </div>
            <button class="btn btn-primary mx-auto">add to cart</button>


        </div>
    </div>
</div>

You would need to import FormModule to make it work.

That answer consumed quite a time. I do hope it would be useful (:

I created an issue to fix the documentation.

Related