I am new to the world of Angular-7, RxJS-6, and Visual Studio Code. Currently, I am facing a challenge with debugging an Observable that is being returned to a subscriber, resulting in a "TypeError" at runtime. It seems like this issue is not uncommon among developers. Can anyone provide guidance on how I can identify what the subscriber is observing or spot any errors in the code below?
In More Detail
I am working on a basic proof of concept using Visual Studio Code and Angular-7 CLI to fetch the current system date/time from a server using Angular's httpclient
and display it.
Take a look at the method instrument.service.ts::getSystemTimeDate() below. The HTTP request is successful as we get the following JSON response:
{
"SystemDateTime": "2018-11-26T08:54:06.894Z"
}
Within the map
operator, this response gets converted first to an object of type SystemDateTimeResponse
and then to a Date
. The goal is to return an Observable<Date>
to subscribers. However, I am struggling with how the component subscribes to this Observable<Date>
. At runtime, the subscriber in the method onTimeDateBtnClick()
throws the error mentioned above:
ERROR
TypeError: You provided an invalid object where a stream was expected...
I suspect that my issue lies in not correctly returning an Observable and possibly messing up the use of the map operator. What am I overlooking here?
The Source Code
The key components in this snippet are:
timedate.component.html: contains a simple template
<p>
Last time checked: {{today | date:'medium'}}
</p>
<button mat-button (click)="onTimedateBtnClick()">Update</button>
timedate.component.ts: defines the display property today
and the event handler onTimedateBtnClick()
which uses a data service for managing HTTP requests/responses to retrieve the current date/time from a server.
import { Component, OnInit } from '@angular/core';
import { InstrumentService } from '../instrument.service';
import { Observable } from 'rxjs';
@Component({
selector: 'app-timedate',
templateUrl: './timedate.component.html',
styleUrls: ['./timedate.component.css']
})
export class TimedateComponent implements OnInit {
/** Display property */
today: Date;
/**
* Constructor
* @param - data service
*/
constructor(private dataService: InstrumentService) {
}
ngOnInit() {
this.today = new Date(); /// initialise with client's date/time
}
/**
* User event handler requesting system time/date from the server
*/
onTimedateBtnClick() {
const http$: Observable<Date> = this.dataService.getSystemTimeDate();
http$.subscribe(
res => this.today = res,
);
}
}
instrument.service.ts: includes the getSystemTimeDate()
method that returns an Observable<Date>
. Though simplified, the code still results in failure to better understand donde exaggerated use of the map operator.
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
// App imports
import { SystemDateTimeResponse, SystemDateTimeUrl } from './instrument.service.httpdtos';
@Injectable({
providedIn: 'root'
})
export class InstrumentService {
constructor(private http: HttpClient) { }
/**
* Return the server date and time
*/
public getSystemTimeDate(): Observable<Date> {
// Convert the server response object into an observable date
const responseObject: Observable<Date> =
this.http.get<SystemDateTimeResponse>(SystemDateTimeUrl.Url).
pipe(
map(jsonResponse => {
const newDto = new SystemDateTimeResponse(jsonResponse.SystemDateTime);
const d = new Date(newDto.SystemDateTime);
return d;
}),
);
return responseObject;
}
}
instrument.service.httpdtos.ts: Contains definitions for data transfer objects.
/** URL - Instrument system date/time */
export class SystemDateTimeUrl {
public static readonly HttpVerb = 'GET';
public static readonly Url = 'api/instrument/systemdatetime';
public static readonly Summary = 'Query the instrument current date/time';
}
/** Response DTO */
export class SystemDateTimeResponse {
constructor(public SystemDateTime: string) {}
}