Angular 2 HTTP Service not returning promise - javascript

I'm trying to get an angular 2 service to retrieve data from an HTTP request and return it as a promise. When I use the service in the component, the data I'm passing from the service is returned as undefined.
This is my service
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class RecordService {
constructor(private http: Http) {}
getPosts(): Promise<any> {
return this.http
.get('https://jsonplaceholder.typicode.com/posts')
.toPromise()
.then((response: Response) => response.json().data)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
console.log('ERROR');
return Promise.reject(error.message || error);
}
}
and this is my component
import { Component, OnInit } from '#angular/core';
import { RecordService } from './record.service';
import { Router } from '#angular/router';
#Component({
selector: 'record-view',
template: '<h1>This is the record creation page</h1>',
providers: [RecordService]
})
export class RecordComponent implements OnInit{
message: string;
error: any;
constructor(private recordService: RecordService) {
}
ngOnInit() {
this.recordService.getPosts()
.then(data => console.log(data))
.catch(error => console.log(error));
}
}
Any ideas why the data would be undefined?

response.json() already gives you back the data object of your response as JSON, so remove the .data property access.

When you response.json() the result is the exact content from the response of the request you made.
In this case, https://jsonplaceholder.typicode.com/posts returns an array (if open the url in a browser you'll see the array): [{...}, {...}, ...].

From response.json().data remove .data and add || {} if body is null
Finally:
.then((response: Response) => response.json() || {})

Related

You provided 'undefined' where a stream was expected. in token interceptor

I am trying to make an interceptor to refresh the token, but it throws me this error and I don't know why
ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
token-interceptor.service.ts
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
import { HttpClient, HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { environment } from 'src/environments/environment';
import { catchError, map} from 'rxjs/operators';
import { throwError } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {
constructor(
private auth: AuthService,
private http: HttpClient
) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.url.includes('signin') || err.url.includes('refreshToken')) {
return next.handle(req)
}
//if error is not about authorization
if (err.status !== 401) {
return next.handle(req)
}
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
} else {
return throwError(err)
}
})
)
}
renewToken(req: HttpRequest<any>) {
return this.http.get(`${environment.API_URL}/refreshToken`, { withCredentials: true }).pipe(
map((res: any) => {
//update access token
this.auth.setToken(res.token)
return req.clone({
setHeaders: {
authorization: `Bearer ${res.token}`
}
})
})
)
}
}
Ignore this: It looks like your post is mostly code; please add some more details. It looks like your post is mostly code; please add some more details.
this piece of code is wrong:
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
istead it should be:
return this.renewToken(req).pipe(switchMap(request => next.handle(request)));
you are just returning nothing in your variant, that is why it doesn't work.
also the whole logic of token interpceptor seems weird to me. I believe you should rethink about how you want it to work. for now as I see you sending request without token and in almost all cases you are sending it again unmodified, and the one that I fixed above will send it again with token. Wouldn't it be right to add token every time, and only send it 2nd time if token is outdated?

Angular - HTTPClientModule delete request not working

I am making a simple delete request from my angular app but nothing is happening and no error is appearing. My service code is as follows :
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class TodoService {
todoUrl = 'https://example.herokuapp.com/api/todoDB/';
constructor(private http: HttpClient) { }
getTodo() {
return this.http.get(this.todoUrl);
}
postTodo(todoObject: any) {
return this.http.post(this.todoUrl , todoObject);
}
deleteTodo(id: any) {
const url = `${this.todoUrl}${id}`;
console.log(url); // *** This is printing correct URL
return this.http.delete(url);
}
}
My getTodo() and postTodo() are working completely fine but the deleteTodo() method is not working and also it does not show any error either. When I put the URL from the console.log(url) in postman, it works but it is not working from my app.I am using the following code in my component to access the deleteTodo() method of my service :
removeTodo(i: any) {
this.todoService.deleteTodo(this.todoArray[i]._id);
}
My delete route of server :
// Delete Todo
router.delete('/:id' , (req , res) => {
Todo.findById(req.params.id)
.then((todo) => todo.remove().then(() => res.json({success : true})))
.catch(err => res.json({success : false}).status(404))
});
You need to subscribe to the Observable
Code Snippet for your problem:
removeTodo(i: any) {
this.todoService.deleteTodo(this.todoArray[i]._id).subscribe(e=>{
// Callback
// Perform Actions which are required after deleting the id from the TODO
});
}
Additional Reference:
https://www.pluralsight.com/guides/posting-deleting-putting-data-angular
https://angular.io/guide/http#making-a-delete-request
Modify your code to support catchError and throwError using pipe for debugging.
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
deleteTodo(id: any) {
const url = `${this.todoUrl}${id}`;
return this.http.delete(url).pipe(
catchError((err) => {
console.log('error caught in service')
console.error(err);
return throwError(err); //Rethrow it back to component
})
);
}

Handle server error while using Observables

i'm working on an Angular 2 Apllication where i have in my login feature this service.
import { Http, Response } from '#angular/http';
import {Injectable} from '#angular/core';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
import { contentHeaders, apiUrl} from '../shared/headers';
#Injectable()
export class LoginService extends BaseService{
constructor(private http: Http){
super();
}
/**
* send the user login data (email, password) and the token back to be stored on the client side.
* #param user
* #returns {any|Promise}
*/
login(user: any): Observable<any>{
let body = JSON.stringify(user);
return this.http.post(apiUrl + '/login', body, { headers: contentHeaders })
.map(this.extractData)
.catch(this.handleError);
}
/**
* extract response data and return it to the component
* #param res
* #returns {*}
*/
public extractData(res: Response) {
let body = res.json();
console.log(body);
return body;
}
/**
* handle service error
* #param error
* #returns {ErrorObservable}
*/
public handleError(res: Response) {
return Observable.throw(res);
}
}
and i use it in my LoginComponent in this way
this.loginService.login(userObj)
.subscribe(
(response: any) => {
// success call that is Ok
},
(errorRes: any)=> {
console.log('res in err is', error);
}
-the result of console.log in my component is
TypeError: Observable_1.Observable.throw is not a function
i tried to search stackoverflow or in github if an issue solves this but i couldn't find soething that helps me, so if someone can help me handle the error in LoginComponent as a response from handleError method of the service and get the error message of the server in my component it will be great.
Note: the success part is working fine the problem is in the case of error when i make
return Observable.throw(res);
thanks in advance
You need to import Observable.throw().
Add this import statement:
import 'rxjs/add/observable/throw';
When ever throw is raised you should handle using catch operator as below
getTasks(): Observable<any[]> {
return this._http.get(this._url)
.map((response: Response) => <any[]>response.json())
.do(data => console.log("data we got is " + JSON.stringify(data)))
.catch(this.handleError);
}
private handleError(error: Response) {
console.log(error);
}
Also you are throwing again in your error Handler method which should not be as such
this.loginService.login(userObj)
.subscribe(
(response: any) => {
// success call that is Ok
},
(error)=> {
////////////////////////error message is available in this object
console.log('res in err is', error);
})

Duplicate http requests sent when using http interceptor (in Ionic 2)

TL;DR;
Why subscribing to an Observable in an http interceptor produces duplicate http requests to server?
Sample code:
doGetWithInterceptor() {
console.log("Http get with interceptor -> 2 http calls ?? Why?");
this.http_interceptor_get("http://ip.jsontest.com/").subscribe(data => {
console.log("But only one block of data received:", data);
this.result= data.ip;
});
}
http_interceptor_get(url : string) {
let req= this.http.get(url).map(res => res.json());
req.subscribe((data) => {
console.log("[HttpInterceptor]");
});
return req;
}
Full details:
I use an http interceptor service in my Ionic 2 project to globally detect errors, authentication, and more...
But doing so, I am seeing duplicate http requests to the server.
I have an small test App starting from a blank Ionic 2 template:
Which clearly shows the problem in Firebug:
First request (it's ok, single) if using the GET button.
Second request (which duplicates) is using the "Get with interceptor" button.
Meanwhile, the code in the subscription part is executed only once, as it should.
The home.ts code is as follows:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
result : string = "???";
constructor(public navCtrl: NavController, public http: Http) {
}
http_get(url : string) {
return this.http.get(url).map(res => res.json());
}
http_interceptor_get(url : string) {
let req= this.http.get(url).map(res => res.json());
req.subscribe((data) => {
console.log("[HttpInterceptor]");
});
return req;
}
doGet() {
console.log("Normal http get -> 1 http call");
this.http_get("http://ip.jsontest.com/").subscribe(data => {
console.log("One block of data received:", data);
this.result= data.ip;
});
}
doGetWithInterceptor() {
console.log("Http get with interceptor -> 2 http calls ?? Why?");
this.http_interceptor_get("http://ip.jsontest.com/").subscribe(data => {
console.log("But only one block of data received:", data);
this.result= data.ip;
});
}
doClearResult() {
this.result= "???";
}
}
Its because you are not really intercepting. You are simply subscirbing to the request twice.
http_interceptor_get(url : string) {
let req= this.http.get(url).map(res => res.json());
req.subscribe((data) => { //1st subscription - 1st call
console.log("[HttpInterceptor]");
});
return req; //return original request
}
Then you are subscribing again in doGetWithInterceptor() to your http req.
If you want to log details of call, you can use do().
http_interceptor_get(url : string) {
//return http call
return this.http.get(url).map(res => res.json())
.do(data=>{
//do checks.
return data; //be sure to return data so it is passed on to subscription.
});
}
Then call in your doGetWithInterceptor()

Angular 2 Http Request to Promise returns zone obj instead of actual data

I'm trying to have quick test of ng 2 http to return real data. I know there is a better/longer way to do it. This is meant to be quick and simple, not best practices.
I know the server returns data because I can see it in another terminal window. The json is very simple {a:b} because it is just a proof of concept.
I don't care if it is a promise or an observable as long as it hangs around to return the real data right there -- so I can figure out that it actually works -- not that I want to write production code that way.
//app.data.service.ts
import { Injectable } from '#angular/core';
import { Http, Response} from '#angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/toPromise';
#Injectable() export class DataService {
constructor(private http: Http) {
}
public getItems(){
return this.http.get('http://localhost:8090/data/config.txt')
.toPromise()
.then(data => Promise.resolve(data.json()));
}
}
// app.data.service.spec.ts
/* tslint:disable:no-unused-variable */
import { AppComponent } from './app.component';
import { TestBed, inject, fakeAsync } from '#angular/core/testing';
import { MockBackend, MockConnection } from '#angular/http/testing';
import { By } from '#angular/platform-browser';
import { HttpModule } from '#angular/http';
import { DataService } from './app.data.service';
describe('DataService', function () {
let dataService: DataService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
declarations: [AppComponent],
providers: [DataService]
});
dataService = TestBed.get(DataService);
});
it('should be instantiated by the testbed', () => {
expect(dataService).toBeDefined();
});
it('should return get', () => {
let data = dataService.getItems();
console.log('test data= ' + data);
console.log('test string(data)= ' + JSON.stringify(data));
});
});
//tail end of tests.html
<tr class="system-out">
<td colspan="3"><strong>System output:</strong><br />Chrome 53.0.2785 (Mac OS X 10.11.6) LOG: 'WARNING: System.import could not load "systemjs.config.extras.js"; continuing without it.'
<br />Chrome 53.0.2785 (Mac OS X 10.11.6) LOG: Error{originalErr: Error{}}
<br />Chrome 53.0.2785 (Mac OS X 10.11.6) LOG: 'test data= [object Object]'
<br />Chrome 53.0.2785 (Mac OS X 10.11.6) LOG: 'test string(data)= {"__zone_symbol__state":null,"__zone_symbol__value":[]}'
</td>
In app.data.service.ts
public getItems(){
return this.http.get("http://......")
.toPromise()
.then(res => res.json())
.catch(this.handleError);
}
In your component.ts call this method/subscribe to it
data:any;
ngOnInit() {
this.appService.getItems()
.then(data => console.log(data));
}
Several issues to fix this, debugging the chrome browser that the karma test popped up helped -
server wasn't returning CORS headers
observable/subscribe code was
not working
json data was {a:b}, when I changed it to {"a":"b"} - the
result.json() worked
For issue #2 the following is the code for getItems:
//app.data.service.ts
getItems(url:string) : Observable<Response> {
return this._http.get(url)
.map((response: Response) => {
return response;
}).catch(this.handleError);
};
//app.data.service.spec.ts
it('should return {a:b}', () => {
let data: string;
dataService.getItems("http://localhost:8090/data/config.json")
.subscribe(
(response) => {
//Here you can map the response to a type
console.log("test getItems returned");
data = JSON.stringify(response.json());
console.log("data = " + data);
},
(err) => {
//Here you can catch the error
console.log("test getItems returned err");
}
);
});

Categories

Resources