Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a service as input in Angular2

Tags:

angular

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.

like image 312
JME Avatar asked Jul 14 '16 16:07

JME


1 Answers

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.

1. Defining services

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.

2. Parent component

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>

3. Generic list component

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.


Full example

https://stackblitz.com/edit/angular-mgubup


I'm looking forward to know if there's another more preferred method to implement this.

like image 73
andreivictor Avatar answered Oct 26 '22 09:10

andreivictor