In the saveExpense() method, when passing error handling catchError (this.handleError<any>('Add Expense', [])), this method is highlighted and the error is displayed: Argument type (error:any)=>Observable<any> is not assignable to parameter type (err:any, caught:Observable<T>)=>never. How to solve?
saveExpense(userid, oExpense) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `${this.jwtToken}`
})
};
return this.http.post(`http://localhost:5555/api/expense/${userid}`, JSON.stringify(oExpense), httpOptions).pipe(
tap(
(response: ServerMessage) => console.log(response)
),
catchError(this.handleError('Add Expense', []))
);
}
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error);
console.log(`${operation} failed: ${error.message}`);
return of(result as T);
};
}
That statement means catchError() is expecting to be given a function with a signature of (err:any, caught:Observable<T>)=>never as a parameter.
What's the signature (parameters and return type) of the function that gets passed to catchError()?
try this:
import { Observable, throwError } from 'rxjs';
saveExpense(userid, oExpense) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `${this.jwtToken}`
})
};
return this.http
.post(`http://localhost:5555/api/expense/${userid}`, JSON.stringify(oExpense), httpOptions)
.pipe(
tap((response: ServerMessage) => console.log(response)),
catchError((err) => this.handleError(err)),
);
}
private handleError(errorResponse): Observable<any> {
console.log('error', errorResponse);
return throwError(errorResponse);
}
I have a complete example of how to work with interceptors:
https://github.com/dedd1993/ngx-admin/blob/master/src/app/%40core/http/http.interceptor.ts
Related
I have service which i pass user token to server and return results to component but it keeps returning token: undefined while my token is exist.
Code
Note: I commented each part for better understanding.
Service
export class GroupsService {
token: any;
constructor(
private storageIonic: NativeStorage,
private env: EnvService,
private http: HttpClient,
) {
// Get token
this.storageIonic.getItem('token').then((token) => {
this.token = token.access_token;
}).catch(error => console.error(error));
}
getGroups(): Observable<any> {
// I also add this here to make sure that i will get token in any case, yet it's returning undefined
if (this.token === undefined) {
this.storageIonic.getItem('token').then((token) => {
this.token = token.access_token;
}).catch(error => console.error(error));
}
console.log('token: ', this.token); // undefined
const httpOptions = {
headers: new HttpHeaders({
Authorization : this.token, //sending token to server
Accept: 'application/json, text/plain',
'Content-Type': 'application/json'
})
};
return this.http.get(`${this.env.GROUPS}`, httpOptions).pipe(
map(groups => groups)
);
}
}
Component
export class Tab1Page implements OnInit {
groups: any[] = [];
groupsOpts = {
loop: false,
slidesPerView: 3,
slidesPerColumn: 2
};
constructor(
private groupsService: GroupsService,
private menu: MenuController,
) {
this.menu.enable(true);
this.getGroups();
}
ngOnInit() {
//
}
// I added async/await yet result hasn't change.
async getGroups() {
await this.groupsService.getGroups().subscribe((res) => {
console.log('res: ', res);
console.log('res data: ', res.data);
console.log('res data data: ', res.data.data);
for (const group of res.data) {
this.groups.push(group);
}
});
}
}
Any idea how to solve this issue?
You can use switchMap to pipe the token promise data.
import { from } from "rxjs";
export class GroupsService {
token: any;
getGroups(): Observable<any> {
// I also add this here to make sure that i will get token in any case, yet it's returning undefined
const tokenPromise =
this.token === undefined
? this.storageIonic.getItem("token")
: Promise.resolve(this.token);
return from(tokenPromise).pipe(
switchMap((token) => {
this.token = token;
const httpOptions = {
headers: new HttpHeaders({
Authorization: this.token, //sending token to server
Accept: "application/json, text/plain",
"Content-Type": "application/json",
}),
};
return this.http
.get(`${this.env.GROUPS}`, httpOptions)
.pipe(map((groups) => groups));
})
);
}
}
You need to wait for the promise to return a value before making the http request. You could put your token logic into its own function that returns a promise, and then start an observable using the RxJS from function. Once the promise returns a value you can then use switchMap to make your http request.
I have included your map in the RxJS pipe, although it's doing nothing at the moment.
export class GroupsService {
token: any;
constructor(
private storageIonic: NativeStorage,
private env: EnvService,
private http: HttpClient,
) {
}
getGroups(): Observable<any> {
return from(this.getToken()).pipe(
switchMap(() => {
const httpOptions = {
headers: new HttpHeaders({
Authorization : this.token, //sending token to server
Accept: 'application/json, text/plain',
'Content-Type': 'application/json'
})
};
return this.http.get(`${this.env.GROUPS}`, httpOptions);
}),
map(groups => groups)
);
}
private getToken(): Promise<any> {
if (this.token) {
return new Promise((resolve, reject) => resolve(this.token));
}
return this.storageIonic.getItem('token')
.then((token) => {
this.token = token.access_token;
}).catch(error => console.error(error));
}
}
this.storageIonic.getItem('token').then((token) => {
this.token = token.access_token;
}).catch(error => console.error(error));
}
This call is asychronous, you will not get the token in the next line
Try it out like this
export class GroupsService {
token: any;
constructor(
private storageIonic: NativeStorage,
private env: EnvService,
private http: HttpClient,
) {
// Get token
this.storageIonic.getItem('token').then((token) => {
this.token = token.access_token;
}).catch(error => console.error(error));
}
getGroups(): Observable < any > {
// I also add this here to make sure that i will get token in any case, yet it's returning undefined
let response = new Observable<any>();
if (this.token === undefined) {
this.storageIonic.getItem('token').then((token) => {
this.token = token.access_token;
console.log('token: ', this.token); // undefined
const httpOptions = {
headers: new HttpHeaders({
Authorization: this.token, //sending token to server
Accept: 'application/json, text/plain',
'Content-Type': 'application/json'
})
};
response = this.http.get(`${this.env.GROUPS}`, httpOptions).pipe(
map(groups => groups)
);
}).catch(error => console.error(error));
}
return response;
}
}
I am not sure about the source of your problem but you may want to change your observables to promises as I have explained in the comments.
getGroups(): Promise<any> {
// I also add this here to make sure that i will get token in any case, yet it's returning undefined
if (this.token === undefined) {
this.storageIonic.getItem('token').then((token) => {
this.token = token.access_token;
}).catch(error => console.error(error));
}
console.log('token: ', this.token); // undefined
const httpOptions = {
headers: new HttpHeaders({
Authorization : this.token, //sending token to server
Accept: 'application/json, text/plain',
'Content-Type': 'application/json'
})
};
return this.http.get(`${this.env.GROUPS}`, httpOptions).toPromise();
// Also probably the below one works too, you can try to find the proper syntax, it was something like this
return this.http.get(`${this.env.GROUPS}`, httpOptions).pipe(
map(groups => groups)
).toPromise();
}
I have changed some lines. (Change method signature, Observable => Promise and add toPromise() to return line)
You can call the method like below.
const response = await getGroups(); // This one will return the response of the request.
If you debug the code you will see that your code will wait here until it gets a response.
// IF YOU DO LIKE BELOW IT WON'T MAKE ANY SENSE
const response = getGroups(); // This will not make the call, it will just return the request object.
// In order to do the operation defined in a promise, you must call it with await prefix.
You may need apply the solution above to other parts of your code too. E.g. you are initializing the token under the constructor which it is not a good practice as I know, you may want to move that initialization under onInit() and make onInit function async. This way you can make sure that token is defined when you are making the call, otherwise your token may not beer initialized while you are making the request. And since you are not waiting your code in undefined check same thing will happen again.
(Convert your storageIonic.getItem(token: string) function to a promise and return the token from that function)
I subscribe to the Observable returned by my HttpClient.put(...), the response comes back null. Here's my code:
constructor(private http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
updateIsEnabled(type: string, isChecked: boolean) {
const url = `${this.isEnabledUrl}?type=${type}&isEnabled=${isChecked}`;
this.http.put(url, this.httpOptions)
.pipe(
catchError(this.handleError('updateIsEnabled'))
).subscribe(response => {
if (response.getStatusCode() == 200) {
//do something
}
});
}
But I get:
ERROR TypeError: Cannot read property 'getStatusCode' of null
I am sure that I'm responding with a 200 OK (Spring backend), but with an empty body. I figured maybe it was expecting a JSON. So I changed the options to:
httpTextOptions = {
headers: new HttpHeaders({ 'Content-Type': 'text/plain',
'Content-Length': '0'})
}
But still with the same results.
Any ideas?
Thanks
I don't get the point of catchError as it destroys the observable, not sure if it is a bug or I don't understand it properly but I have learned not to use it as it isn't useful for catching errors.
const { throwError } = rxjs;
const { catchError } = rxjs.operators;
throwError('error').pipe(
catchError(e => { console.log(e); })
).subscribe(val => { console.log(val); }, e => { console.log(e); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
You can get the error with a tap and an error handler that doesn't destroy the observable
const { throwError } = rxjs;
const { tap } = rxjs.operators;
throwError('error').pipe(
tap(undefined, e => { console.log(e); })
).subscribe(val => { console.log(val); }, e => { console.log(e); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
So i'm trying to build this action on a react and i need it as a promise.
The action succeeds, i receive the response from the server but i also get an error saying:
VM89852:98 Uncaught TypeError: Cannot read property 'then' of undefined.
action:
export const fetchAllAccounts = (token, dispatch) => {
return new Promise((resolve, reject) => {
fetchAccountsStart((dispatch));
return axios.get(`${API_URL}/accounts`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}).then(
(response) => {
fetchAccountsSuccess(dispatch, response.data);
resolve(response.data);
},(error) => {
fetchAccountsFailed(dispatch, error.data);
reject(error.data);
},
);
});
};
Also heres the method on how i call this action.
this.props.fetchAllAccounts(token)
.then((data) => {
console.log("#".repeat(120));
console.log(data);
console.log("#".repeat(120));
}).catch((error) => {
console.log("#".repeat(120));
console.log(error);
console.log("#".repeat(120));
});
your comment
heres the call from mapDispatchToProps ...
fetchAllAccounts: (token) => { fetchAllAccounts(token, dispatch) },
There is your problem, in the comment. This either needs to be
fetchAllAccounts: (token) => { return fetchAllAccounts(token, dispatch) },
or
fetchAllAccounts: (token) => fetchAllAccounts(token, dispatch),
Understand that with arrow functions, if you use {} you need to return, there is no implied return
As a bonus - remove the promise constructor anti-pattern
export const fetchAllAccounts = (token, dispatch) => {
fetchAccountsStart((dispatch));
return axios.get(`${API_URL}/accounts`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}).then(
(response) => {
fetchAccountsSuccess(dispatch, response.data);
return response.data;
}, (error) => {
fetchAccountsFailed(dispatch, error.data);
throw error.data;
// Borrowed from #T.J.Crowder's pastebin :p
// Note that it's best to reject or throw an Error instance,
// not other types; e.g., `throw new Error(error.data)` or
// `return Promise.reject(new Error(error.data))`
},
);
};
getNews(newsType : any){
this.storage.get("USER_INFO").then(right=>{
this.storage.get("sessionkey").then(temp=>{
this.email = JSON.parse(right).email;
this.newkey = temp;
this.authentification =JSON.stringify("Basic " + btoa(this.email+":"+ this.newkey+":"+key));
const body = newsType;
let headers = new Headers({
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': this.authentification
});
let options = new RequestOptions({headers : headers});
return this.http.post('http://api/getNews',body,options)
.map((data:Response) => data.json());
}, err =>{console.log("error on sessionkey",err)})
}, err =>{console.log("error on user",err)})
}
this.httpService.getNews(JSON.stringify(this.category)).subscribe(data => {
this.news = data.News;
});
}, err => {
console.log("Error:", err)
});
I want to call the Api after the success of nested functions.
But when i performing it in the function success callback it it giving me error that Property 'subscribe' does not exist on type 'void'.
How can I return the value of api from service to another .ts file
You're missing return statement here:
getNews(newsType : any){
return this.storage.get('USER_INFO').then(right => {
^^^^^^
However, that would still return a promise with no subscribe method. To wrap a promise result into an observable, you can use from method:
getNews(newsType : any){
return Observable.from(this.storage.get('USER_INFO').then(right => {
This is the solution which worked for me.
this.httpService.getNews(JSON.stringify(this.category)).subscribe(data => {
this.news = data.News;
}}, err => {
console.log("Error:", err)
});
getNews(newsType :any) : Observable<any> {
return Observable.create(observer => {
this.storage.get("USER_INFO").then(right=>{
this.storage.get("sessionkey").then(temp=>{
this.email = JSON.parse(right).email;
let authentification =JSON.stringify("Basic " + btoa(this.email+":"+ temp+":"+key));
const body = newsType;
let headers = new Headers({
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': authentification
});
let options = new RequestOptions({headers : headers});
this.http.post('http:api/getNews',body,options).subscribe(data =>{
observer.next(data.json());
observer.complete();
});
}, err =>{console.log("error on sessionkey",err)})
}, err =>{console.log("error on user",err)})
}) }
Thanks #Maximus and #Theophilus Omoregbee
link: How to create an observable in Angular 2
I am using ionic 2 / angular 2.
I need to do a http request, but before I have to get a token using Ionic Storage.
I created a class ApiRequest for that
import {Http, Headers, RequestOptions} from '#angular/http';
import {Injectable} from '#angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Storage } from '#ionic/storage';
#Injectable()
export class ApiRequest {
access_token: string;
constructor(private http: Http, public storage: Storage) {
this.storage.get('access_token').then( (value:any) => {
this.access_token = value;
});
}
get(url) {
let headers = new Headers({
// 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.access_token,
'X-Requested-With': 'XMLHttpRequest'
});
let options = new RequestOptions({ headers: headers });
return this.http.get(url, options)
.map(res => res.json());
}
}
Then I can call like that
apiRequest.get(this.URL)
.subscribe(
data => {
this.element= data;
},
err => {
console.log(JSON.stringify(err));
});
My problem is, this.storage.get is asynchronous, http.get is asynchronous too, and I have to return http.get because I want to call subscribe outside the function.
In this case http.get is called before this.acess token received the value.
How Can I organize my code in that scenario?
This might work (not tried myself):
#Injectable()
export class ApiRequest {
access_token: string;
constructor(private http: Http, public storage: Storage) {
this.storagePromise = this.storage.get('access_token').then( (value:any) => {
this.access_token = value;
});
}
get(url) {
let headers = new Headers({
// 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.access_token,
'X-Requested-With': 'XMLHttpRequest'
});
let options = new RequestOptions({ headers: headers });
return this.storagePromise.then(
return token => this.http.get(url, options)
.map(res => res.json());
);
}
}
apiRequest.get(this.URL)
.then(observable =>
observable.subscribe(
data => {
this.element= data;
},
err => {
console.log(JSON.stringify(err));
}
);