I want to create a generic list component that contains several features I want all my lists to have. Ideally I would like to use different services as input to the component to render different data sets. Is this possible in angular 2?
Modfying the example from the angular 2 docs
@Component({
selector: 'Generic List',
template:
`
<h2>Items</h2>
<ul class="items">
<li *ngFor="let item of items"
[class.selected]="isSelected(item)"
(click)="onSelect(item)">
<span class="badge">{{item.id}}</span> {{item.name}}
</li>
</ul>
`
})
export class GTComponent {
@Input() someService: Service;
items: Item[];
private selectedId: number;
isSelected(item: Item) { return item.id === this.selectedId; }
onSelect(item: Item) {
}
}
If this is possible, how would I populate the items array with data from the service if the service has some function named getItems()
that returns a promise.
I had the same need and in the end I managed to do the implementation, which so far seems to work well. The implementation is written in Angular 8.
All the services that will be passed as @Input
to the child component should have a method called getItems()
, so, firstly, I've created an abstract class that holds nothing but the definitions of the method. This class is really no more than a container that informs Angular about the getItems()
method:
@Injectable()
export abstract class ItemsService {
constructor() { }
/**
* Returns a list of all items
*/
abstract getItems(): Observable<Item[]>;
}
Then, we can create our services (ItemsAService
, ItemsBService
, ItemsCService
), that will implement ItemsService
, as follows:
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { ItemsService } from './items.service';
import { Item } from './item';
const ITEMS_A: Item[] = [
// our items collection
];
@Injectable()
export class ItemsAService implements ItemsService {
constructor() { }
getItems(): Observable<Item[]> {
return of(ITEMS_A);
// of(ITEMS_A) returns an Observable<Item[]> that emits the array of mock items.
// in real life, you'll call HttpClient.get<Item[]>() which also returns an Observable<Item[]>:
// return this.httpClient.get<Item[]>
}
}
... same code for ItemsBService
and ItemsCService
.
More info about creating interfaces for Angular Services here.
Our generic list component will be included from a parent component. In this parent component, we'll inject the services described above, as follows:
import { Component } from '@angular/core';
import { ItemsAService } from './items-a.service';
import { ItemsBService } from './items-b.service';
import { ItemsCService } from './items-c.service';
@Component({
...
})
export class AppComponent {
constructor(
public itemsAService: ItemsAService,
public itemsBService: ItemsBService,
public itemsCService: ItemsCService
) { }
}
Then, in template, we have:
<app-generic-list
name="A"
[service]="itemsAService"
>
</app-generic-list>
<app-generic-list
name="B"
[service]="itemsBService"
>
</app-generic-list>
<app-generic-list
name="C"
[service]="itemsCService"
>
</app-generic-list>
This component we'll have an @Input() service
, type ItemsService
and we'll call the getItems()
method from the service in the ngOnInit()
hook:
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { ItemsService } from '../items.service';
import { Item } from '../item';
@Component({
selector: 'app-generic-list',
...
})
export class GenericListComponent implements OnInit, OnDestroy {
@Input() name: string;
@Input() service: ItemsService;
items: Item[];
subscription: Subscription;
constructor() { }
ngOnInit() {
if (!this.service) return;
this.subscription = this.service.getItems()
.subscribe(items => this.items = items);
}
// other methods here
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Note that the parent component and the child (generic list) component will share the same instance of the service.
https://stackblitz.com/edit/angular-mgubup
I'm looking forward to know if there's another more preferred method to implement this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With