Hone logo
Hone
Problems

Angular Dependency Injection: Building a Flexible Data Service

This challenge focuses on implementing Dependency Injection (DI) in Angular to create a reusable and flexible data service. You will learn how to inject services into components and other services, making your application more modular and testable. This is a fundamental concept in Angular development.

Problem Description

Your task is to create a simple Angular application that demonstrates dependency injection. You will build a DataService responsible for fetching and providing some mock data. This DataService will then be injected into a ListComponent and a DetailComponent to display this data.

Requirements:

  1. Create a DataService:

    • This service should have a method, getData(), that returns an observable of an array of mock data objects. Each object should have at least an id (number) and a name (string).
    • The DataService should be provided at the root level of the application so it's a singleton.
  2. Create a ListComponent:

    • This component should inject the DataService.
    • In its ngOnInit lifecycle hook, it should call getData() from the injected DataService.
    • It should then display a list of the fetched data items, showing only their name property.
  3. Create a DetailComponent:

    • This component should also inject the DataService.
    • It should have an @Input() property named itemId (number).
    • In its ngOnInit lifecycle hook, it should call getData() from the injected DataService.
    • It should then filter the fetched data to find the item whose id matches the itemId input and display its name property.
  4. Wire up the components in AppComponent:

    • AppComponent should display both ListComponent and DetailComponent.
    • ListComponent should be displayed by default.
    • DetailComponent should be displayed with a hardcoded itemId of 2 (for demonstration purposes).

Expected Behavior:

  • When the application loads, ListComponent should display the names of all mock data items.
  • DetailComponent should display the name of the data item with id 2.
  • Crucially, both components should be using the same instance of the DataService.

Edge Cases/Considerations:

  • Ensure the observable returned by getData() correctly emits the data.
  • Handle potential errors if the observable were to fail (though for this mock data, it's unlikely).

Examples

Example 1: DataService Structure

// data.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

interface DataItem {
  id: number;
  name: string;
}

@Injectable({
  providedIn: 'root' // Provided at the root level
})
export class DataService {
  private mockData: DataItem[] = [
    { id: 1, name: 'Apple' },
    { id: 2, name: 'Banana' },
    { id: 3, name: 'Cherry' }
  ];

  getData(): Observable<DataItem[]> {
    // Simulate fetching data
    return of(this.mockData);
  }
}

Example 2: ListComponent Structure

// list.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service'; // Assuming data.service is in the parent directory

interface DataItem {
  id: number;
  name: string;
}

@Component({
  selector: 'app-list',
  template: `
    <h2>All Items:</h2>
    <ul>
      <li *ngFor="let item of items">{{ item.name }}</li>
    </ul>
  `
})
export class ListComponent implements OnInit {
  items: DataItem[] = [];

  constructor(private dataService: DataService) { } // Injecting DataService

  ngOnInit(): void {
    this.dataService.getData().subscribe(data => {
      this.items = data;
    });
  }
}

Example 3: DetailComponent Structure

// detail.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { DataService } from '../data.service';

interface DataItem {
  id: number;
  name: string;
}

@Component({
  selector: 'app-detail',
  template: `
    <h2>Item Detail:</h2>
    <p *ngIf="selectedItem">{{ selectedItem.name }}</p>
    <p *ngIf="!selectedItem">Item not found.</p>
  `
})
export class DetailComponent implements OnInit {
  @Input() itemId!: number;
  selectedItem: DataItem | undefined;

  constructor(private dataService: DataService) { } // Injecting DataService

  ngOnInit(): void {
    this.dataService.getData().subscribe(data => {
      this.selectedItem = data.find(item => item.id === this.itemId);
    });
  }
}

Example 4: AppComponent Template

<!-- app.component.html -->
<h1>Dependency Injection Demo</h1>
<app-list></app-list>
<hr>
<app-detail [itemId]="2"></app-detail>

Constraints

  • The solution must be implemented using Angular and TypeScript.
  • No external libraries beyond Angular's core modules and RxJS should be used.
  • The DataService must be provided at the root level (providedIn: 'root').
  • The mock data should consist of at least 3 items.

Notes

  • Focus on understanding how to import and inject services into components.
  • Pay attention to the @Injectable() decorator and its providedIn property.
  • RxJS observables are used here for asynchronous data fetching simulation. Make sure you're comfortable with basic observable subscription.
  • You will need to create the necessary Angular modules and components (e.g., app.module.ts, app.component.ts, data.service.ts, list.component.ts, detail.component.ts).
Loading editor...
typescript