dcp3450
dcp3450

Reputation: 11187

Subscribing to a service from component in Angular 2+

I have a service that needs to return the date every second:

import { Injectable } from '@angular/core';

import {Observable} from 'rxjs/Observable';

@Injectable()
export class ClockService {

    mydate: Date;

  constructor() {
  }

  getClock(): Observable<any> {
      setInterval(()=>{
          this.mydate = new Date();
          return this.mydate;
      }, 1000);
  }

}

I'm trying to subscribe to the getClock() function to update mydate in my component:

import { Component, OnInit } from '@angular/core';
import {ClockService} from './clock.service';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {



  constructor(private clockService: ClockService) { }

  ngOnInit() {
      console.log(this.clockService.getClock().subscribe(res => this.mydate = res));
  }

}

I'm getting an error of, ERROR TypeError: Cannot read property 'subscribe' of undefined

I'm new to Angular 2 and the docs aren't helping.

Upvotes: 1

Views: 364

Answers (3)

msanford
msanford

Reputation: 12247

There's no need to implement setInterval with Rx.

Here's another option with Observable.interval which creates an Observable that emits a new value at the specified interval, which is exactly what you want to do:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';

@Injectable()
export class ClockService {    
  getClock(): Observable<Date> {
      return Observable.interval(1000).map(() => new Date());
  }
}

See it in StackBlitz

Observable.interval() will emit a series of integers 1, 2, 3, ..., whose value you simply discard (() =>) and replace with a Date, using .map().

You can keep your HeaderComponent unchanged.

Note: I've removed the assignment to this.mydate because it doesn't seem to be needed. I've noticed that many people moving to angular create unnecessary intermediary member variables out of reflex, when a purely functional solution suffices. If I've been overly presumptuous and you need it, you can always put it back.

You also don't need an empty constructor in Angular, so I removed that too.

You do need the two additional imports, though.

Upvotes: 2

Dekryptonite
Dekryptonite

Reputation: 13

You are not actually returning an observable but instead void in getClock().

See https://angular.io/guide/observables

Instead do something like this:

import { Injectable } from '@angular/core';

import {Observable} from 'rxjs/Observable';

@Injectable()
export class ClockService {

  private $timer = new Observable(obs => {
      setInterval(() => {
          obs.next(new Date());
      }, 1000);
  });

  constructor() {
  }

  getClock(): Observable<Date> {
      return this.$timer;
  }

}

Upvotes: 1

Pengyy
Pengyy

Reputation: 38189

You should return an Observable at your service function first.

getClock(): Observable<any> {
  return new Observable(observer => {
    setInterval(()=>{
      this.mydate = new Date();
      observer.next(this.mydate);
    }, 1000);
  });
}

Upvotes: 2

Related Questions