Hone logo
Hone
Problems

Building a Reusable "Star Rating" Custom Element in Angular

This challenge focuses on leveraging Angular's capabilities to create a custom, reusable UI component that adheres to the Web Components standard. You will build a "Star Rating" component that can be used within any Angular application and, importantly, can also be rendered as a native Custom Element in environments outside of Angular. This demonstrates a powerful pattern for creating framework-agnostic, highly reusable UI pieces.

Problem Description

Your task is to create a custom Angular component that functions as a star rating system. This component should:

  • Display a configurable number of stars (defaulting to 5).
  • Allow users to select a rating by clicking on individual stars.
  • Visually indicate the selected rating (e.g., by filling in stars up to the selected value).
  • Emit an event when the rating changes.
  • Be packaged and exposed as a standard Web Component (Custom Element) using Angular Elements.

This is useful for creating modular, reusable UI elements that can be easily integrated into different projects and frameworks, promoting code sharing and consistency.

Key Requirements

  1. Angular Component Development:

    • Create an Angular component named StarRatingComponent.
    • The component should accept an @Input() property, maxRating, to define the total number of stars to display (default to 5).
    • The component should emit an @Output() event, ratingChange, of type EventEmitter<number>, whenever the user selects a new rating.
    • The component should visually highlight the stars up to the currently selected rating.
    • Users should be able to interact with the component by clicking on stars to set a rating.
  2. Angular Elements Integration:

    • Configure your Angular project to build the StarRatingComponent as a standalone Custom Element.
    • Ensure the Custom Element can be registered and used in a plain HTML file without requiring the full Angular bootstrapping process for basic rendering.
  3. Styling:

    • Provide basic CSS for the stars to be visually distinct and interactive. Consider how the selected and unselected states should appear.

Expected Behavior

  • When the StarRatingComponent is rendered, it should display maxRating number of unselected stars.
  • When the user hovers over a star, subsequent stars up to the hovered star should visually change (e.g., to a different color or outline) to indicate the potential rating.
  • When the user clicks on a star, the component should update its internal state to reflect the chosen rating, and the ratingChange event should be emitted with the selected rating.
  • The component should retain the selected rating visually even after the hover effect is removed.
  • When used as a Custom Element in HTML, it should be self-contained and function correctly.

Edge Cases

  • What happens if maxRating is set to 0 or a negative number? (The component should handle this gracefully, perhaps by not rendering any stars or defaulting to a sensible value).
  • What happens if the user clicks outside the stars after hovering? (The rating should not change unless a star is actually clicked).

Examples

Example 1: Basic Usage in Angular

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h1>Star Rating Example</h1>
    <app-star-rating [maxRating]="5" (ratingChange)="onRatingChange($event)"></app-star-rating>
    <p>Current Rating: {{ currentRating }}</p>
  `
})
export class AppComponent {
  currentRating: number = 0;

  onRatingChange(rating: number) {
    this.currentRating = rating;
    console.log('Rating changed to:', rating);
  }
}

Output:

The StarRatingComponent will render 5 empty stars. Upon clicking, for instance, the 3rd star, the first three stars will become visually "filled," and the currentRating property in AppComponent will be updated to 3. The console will log "Rating changed to: 3".

Example 2: Using the Custom Element in HTML

<!DOCTYPE html>
<html>
<head>
  <title>Custom Element Test</title>
  <!-- Assume your Angular build output (e.g., runtime.js, polyfills.js, main.js) is linked here -->
  <script src="path/to/your/angular-elements.js"></script>
</head>
<body>
  <h1>Custom Star Rating Element</h1>
  <!-- Note: The selector 'star-rating' must match how you define it in Angular -->
  <star-rating max-rating="7"></star-rating>

  <script>
    // Example of listening to the custom event
    const starRatingElement = document.querySelector('star-rating');
    starRatingElement.addEventListener('ratingChange', (event) => {
      console.log('Custom Element Rating Changed:', event.detail);
    });
  </script>
</body>
</html>

Output:

A star rating component displaying 7 stars will be rendered on the page. When a user interacts with it, the stars will update visually, and the ratingChange custom event will be dispatched with the selected rating as event.detail. The browser console will log "Custom Element Rating Changed: [selectedRating]".

Example 3: Edge Case - maxRating of 0

Input: Angular Component: <app-star-rating [maxRating]="0"></app-star-rating>

Output: No stars will be rendered, or an empty space will be displayed, as the maximum rating is zero. The component should not throw an error.

Constraints

  • Angular Version: Use Angular 14 or later.
  • TypeScript Version: Use TypeScript 4.7 or later.
  • Angular Elements: You must use Angular Elements to export the component as a Custom Element.
  • Styling: Basic CSS is sufficient. Avoid complex animations or dependencies on external CSS frameworks for the core functionality.
  • Performance: The component should be performant for a reasonable number of stars (e.g., up to 20).

Notes

  • You'll need to set up your Angular project to build and bundle Angular Elements. This typically involves creating a separate build or configuration for exporting the custom element.
  • Consider how to handle accessibility for your custom element (e.g., ARIA attributes).
  • When building the Angular Element, pay attention to how custom event details are passed. CustomEvent in JavaScript often uses a detail property. Angular Elements handles this for you by default.
  • Think about the visual representation of stars. You can use text characters (like ★ and ☆), SVGs, or font icons.
  • The input properties for custom elements in HTML are typically written in kebab-case (e.g., max-rating), while Angular uses camelCase in the component's TypeScript (e.g., maxRating). Angular Elements handles this mapping automatically.
Loading editor...
typescript