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:
-
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 anid(number) and aname(string). - The
DataServiceshould be provided at the root level of the application so it's a singleton.
- This service should have a method,
-
Create a
ListComponent:- This component should inject the
DataService. - In its
ngOnInitlifecycle hook, it should callgetData()from the injectedDataService. - It should then display a list of the fetched data items, showing only their
nameproperty.
- This component should inject the
-
Create a
DetailComponent:- This component should also inject the
DataService. - It should have an
@Input()property nameditemId(number). - In its
ngOnInitlifecycle hook, it should callgetData()from the injectedDataService. - It should then filter the fetched data to find the item whose
idmatches theitemIdinput and display itsnameproperty.
- This component should also inject the
-
Wire up the components in
AppComponent:AppComponentshould display bothListComponentandDetailComponent.ListComponentshould be displayed by default.DetailComponentshould be displayed with a hardcodeditemIdof2(for demonstration purposes).
Expected Behavior:
- When the application loads,
ListComponentshould display the names of all mock data items. DetailComponentshould display the name of the data item withid2.- 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
DataServicemust 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 itsprovidedInproperty. - 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).