Skip to content

How to use with FeathersJS

Tobias Fuhrimann edited this page Sep 19, 2016 · 15 revisions

FeathersJS exposes its backend services as a REST API or as a websocket API. To consume the exposed websockets from our Angular2 app, it makes sense to create Angular services to abstract the respective Feathers services in a way that makes it easy for our components to consume them. First we create a base service to inherit from which contains the basic properties of a backend service as a class:

// common/services/api.ts
import { Injectable } from '@angular/core';

@Injectable()
export class APIService {
  private _url: string = 'https://your-api-base-url.com';

  get url(): string {
    return this._url;
  }

}

Then we create the actual service which connects to the FeathersJS backend and exposes it as a service in Angular. It inherits from the base service we've created above. The FeathersJS service is exposed as an rxjs Obersvable which components can then subscribe to:

// my-module/services/my-service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

import { MyModel } from '../models/my-model';

import { APIService } from '../../common/services/api';

declare const io;
declare const feathers;

@Injectable()
export class MyService extends APIService {
  public item$: Observable<any>;
  private feathersService: any;
  private itemsObserver: any;
  private dataStore: {
    items: MyModel[]
  };

  constructor() {
    super();

    const socket = io(this.url);
    const feathersApp = feathers().configure(feathers.socketio(socket));
    this.feathersService = feathersApp.service('item');

    this.feathersService.on('created', (newItem) => this.created(newItem));
    this.feathersService.on('removed', (removedItem) => this.removed(removedItem));

    this.items$ = new Observable(observer => this.itemsObserver = observer)
      .share();

    this.dataStore = { items: [] };
  }

  public find(): void {
    this.feathersService.find((err, items: MyModel[]) => {
      if (err) return console.error(err);

      this.dataStore.items = items;
      this.itemsObserver.next(this.dataStore.items);
    });
  }

  private getIndex(id: string): number {
    let foundIndex = -1;

    for (let i = 0; i < this.dataStore.items.length; i++) {
      if (this.dataStore.items[i].id === id) {
        foundIndex = i;
      }
    }

    return foundIndex;
  }

  private created(newItem: MyModel): void {
    const index = this.getIndex(newItem.id);

    if (index === -1) {
      this.dataStore.items.push(newItem);
    } else {
      this.dataStore.items[index] = newItem;
    }

    this.itemsObserver.next(this.dataStore.items);
  }

  private removed(removedItem): void {
    const index = this.getIndex(removedItem.id);

    this.dataStore.checks.splice(index, 1);

    this.itemsObserver.next(this.dataStore.items);
  }
}

Now you might ask, where we get the two variables which are declared above (io and feathers). These are injected via the respective libraries. To do so, we need to add them to our webpack setup by running npm install --save socket.io-client feathers-client. Then we add them to the plugins section of our webpack.*.config.js files like so:

new ProvidePlugin({
  io: 'socket.io-client',
  feathers: 'feathers-client'
})

If you aren't importing the ProvidePlugin for the respective file yet, you'll have to do so by adding var ProvidePlugin = require('webpack/lib/ProvidePlugin'); to the top of that file.

Now, our Angular service is ready. To use the Observable it exposes in an Angular component, follow the structure below:

// my-module/components/my-component.ts

import { ChangeDetectorRef, ChangeDetectionStrategy, Component } from '@angular/core';
import { Subscription } from 'rxjs';

import { MyModel } from './models/my-model';

import { MyService } from './services/my-service';

@Component({
  selector: 'my-component',
  providers: [MyService],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: require('./my-component.template.html')
})
export class MyComponent {
  public items: MyModel[] = [];
  private subscription: Subscription;

  constructor(
    private myService: MyService,
    private ref: ChangeDetectorRef
  ) {
  }

  private ngOnInit(): void {
    this.subscription = this.myService.items$.subscribe((items: MyModel[]) => {
      this.items = items;
      this.ref.markForCheck();
    });
    this.myService.find();
  }

  private ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}