Create your own Angular control - rate stars - with dynamic input values

<div class="rating">
<div style="display: inline-block"
  *ngFor="let starred of stars; let i = index"
  (click)="rate(i + (starred ? (value > i + 1 ? 1 : 0) : 1))">
      <ng-container *ngIf="starred; else noStar"><mat-icon class="filled">star</mat-icon></ng-container>
      <ng-template #noStar><mat-icon class="empty">star_outline</mat-icon></ng-template>
    </div>
</div>   


 @Component({
  selector: 'jfg-star-rating',
  templateUrl: './star-rating.component.html',
  styleUrls: ['./star-rating.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => StarRatingComponent),
      multi: true
    }
  ]

})
export class StarRatingComponent implements ControlValueAccessor{

  stars: boolean[] = Array(3).fill(true);

   // Allow the input to be disabled, and when it is make it somewhat transparent.
  @Input() disabled = false;
   @HostBinding('style.opacity')
   get opacity() {
     return this.disabled ? 1 : 1;
   }

   // Function to call when the rating changes.
   onChange = (rating: number) => {
   };

   // Function to call when the input is touched (when a star is clicked).
   onTouched = () => {
   };


   get value(): number {
     if(!this.disabled){
     return this.stars.reduce((total, starred) => {
       return total + (starred ? 1 : 0);
     }, 0);
     }
   }
   rate(rating: number) {
     if (!this.disabled) {
       this.writeValue(rating);
     }
   }

   // Allows Angular to update the model (rating).
   // Update the model and changes needed for the view here.
   writeValue(rating: number): void {
     if (!this.disabled) {
       this.stars = this.stars.map((_, i) => rating > i);
       this.onChange(this.value);
     }

   }

   // Allows Angular to register a function to call when the model (rating) changes.
   // Save the function as a property to call later here.
   registerOnChange(fn: (rating: number) => void): void {
     this.onChange = fn;
   }

   // Allows Angular to register a function to call when the input has been touched.
   // Save the function as a property to call later here.
   registerOnTouched(fn: () => void): void {
     this.onTouched = fn;
   }

   // Allows Angular to disable the input.
   setDisabledState(isDisabled: boolean): void {
     this.disabled = isDisabled;


   }
 }

Developing a star rating component that functions well as an input. The component accurately captures user ratings through star clicks which are then passed along to services. However, encountering an issue where setting the number of stars based on an input value hasn't yielded results. Seeking advice on how to proceed in effectively setting the star value from the input provided. Appreciate any suggestions!

Answer №1

If you have already implemented the ControlValueAccessor, you can easily set the value using ngModel for two-way binding. There is no need for any other input to set the value. You can simply use your StarRatingComponent like this -

<jfg-star-rating [ngModel]="3"></jfg-star-rating>

Alternatively, you can use it with two-way binding like this -

<jfg-star-rating [(ngModel)]="rating"></jfg-star-rating>

You can see a working example here - https://stackblitz.com/edit/angular-material-sample-vv4s6b

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for creating CSS3 animations in Angular triggered by mouse clicks

I'm working with a span that utilizes a Bootstrap icon. My goal is to fade out and fade in the same element (span) on click, while toggling the class (icon). There's a boolean variable called showLegend which determines whether or not to animate ...

How to use Angular2 Router to redirect a state to its default substate

How can we implement a default substate in the new Angular2 Router? For instance, I would like the router to automatically direct from /user to /user/profile, creating a default substate for user. ...

How to deactivate Kendo Dropdownlist in Angular 2

Currently, I am attempting to disable a kendo-dropdownlist (named ddlChargeType). The goal is to prevent the user from manually selecting a value, while still allowing for programmatic selection (such as when a valid option is chosen in another dropdown, ...

How do I utilize the file handler to execute the flush method within the Deno log module using Typescript?

I'm having trouble accessing the fileHandler object from my logger in order to flush the buffer to the file. This is the program I am working with: import * as log from "https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_emai ...

Creating a dynamic path to an imported file in React: A step-by-step guide

Struggling with a dilemma regarding dynamically generated paths for importing files in React. I have utilized the map() function to generate a dynamic part of the code, consisting of a repetitive sequence of div elements, each housing an audio element. The ...

Exploring the filter method in arrays to selectively print specific values of an object

const array = [ { value: "Value one", label: "Value at one" }, { value: "Value 2", label: "Value at 2" }, { value: "" , label: "Value at 3" } ...

Exploring: Strategies for patiently waiting for a webpage to refresh

Exploring Angular 15 with karma and jasmine When I navigate from one page to another by clicking a button, the TD tags on the new page are not being displayed in my tests. beforeEach(async () => { await TestBed.configureTestingModule({ decla ...

Unable to perform a default import in Angular 9 version

I made adjustments to tsconfig.json by adding the following properties: "esModuleInterop": true, "allowSyntheticDefaultImports": true, This was done in order to successfully import an npm package using import * as ms from "ms"; Despite these changes, I ...

Revitalize access token with Keycloak in Javascript

I am currently working with keycloak-js version 8.0.1 and have a function called getToken that checks if the token is expired. If it is expired, the function refreshes it; otherwise, it returns the current token. The issue I am facing is that even though t ...

"A collection of elements in Typescript that is uniform in type, denoted by

Is it possible to declare an array of type any[] where all elements are of the same type? For example: // Allowed const array1: any[] = [1, 2, 3]; const array2: any[] = ['a', 'b', 'c']; // Not allowed because it contains bot ...

Properly configuring paths in react-native for smooth navigation

When working on my React-Native project, I noticed that my import paths look something like this: import { ScreenContainer, SLButton, SLTextInput, } from '../../../../../components'; import { KeyBoardTypes } from '../../../../../enums ...

The issue of excessive recursion in Typescript

Currently, I am in the process of learning Typescript while working on some exercises. While attempting to solve a particular problem, I encountered an error related to excessive recursion. This issue arises even though I created wrapper functions. About ...

The argument represented by 'T' does not align with the parameter represented by 'number' and therefore cannot be assigned

I am confused as to why, in my situation, <T> is considered a number but cannot be assigned to a parameter of type number. Changing the type of n to either number or any resolves the issue. Error: https://i.sstatic.net/h1GE9.png Code: const dropF ...

Error encountered during installation of Nativescript Post Install Script

While I am comfortable running one of our current projects with Nativescript, I encountered an error when attempting to install it on a new project using the following command: sudo ng new --collection=@nativescript/schematics the-juice-box --shared The ...

Utilizing the Loess npm module in conjunction with Angular 4

I am attempting to incorporate the Loess package into my project. The package can be found on NPM and offers various regression models for data fitting. I successfully installed it using npm install loess --save, and it now resides in the node_modules dire ...

What is the most efficient way to retrieve a single type from a union that consists of either a single type or an array of types

Is there a way to determine the type of an exported union type by extracting it from an array, as illustrated in the example above? How can this be achieved without directly referencing the non-exported Type itself? interface CurrentType { a: string; b ...

The variable 'cache' is not recognized by Angular

Everything runs smoothly on my local Angular app, but once deployed on Heroku using a Go server, I encounter issues with aot being disabled in the Angular build on Chrome and Opera, particularly on mobile devices using Linux and OSX. However, Safari presen ...

Discovering the versatility of Typescript objects

I want to define a type that follows this rule: If the property container is present, then expect the property a. If the property item is present, then expect the property b. Both container and item cannot exist at the same time. The code I would expect ...

What are the steps to execute jest in an AWS Lambda environment?

I'm looking to execute my end-to-end test post-deployment for the ability to revert in case of any issues. I've followed the guidelines outlined in this particular blog post. Below is my lambda function: export async function testLambda(event: A ...

Dealing with custom path problems in Angular 2+ webpack configurations

I am interested in using the @ngneat/tailwind schematics to convert an Angular project into one with a custom webpack configuration. However, after adding this, my scss import paths for fonts and other partial scss files are not resolving, resulting in th ...