I'm trying to get JSON data using a Service class.
Service class
import { Injectable } from '#angular/core';
import { Http, HttpModule, Response } from '#angular/http';
import { GeneralTab } from './generalTab';
import 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class GeneralTabService {
constructor(private _http : Http) {
console.log("Http call");
}
getStatus(): Observable<any> {
return this._http.get('http://samd.server.lan.at/taskmanagement/rest/taskconfigs/IND?language=EN&access_token=200')
.map((res:Response) => <GeneralTab[]>res.json())
.do(data => console.log("All: " + JSON.stringify(data)))
.catch(this.handleError);
}
private handleError (error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
return Observable.throw(errMsg);
}
}
Component class
import { Component, OnInit } from '#angular/core';
import {GeneralTab} from "./generalTab";
import {GeneralTabService} from "./generalTab.service";
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'general-tab',
templateUrl: '/general.component.html',
providers : [GeneralTabService]
})
export class GeneralComponent implements OnInit{
title = 'Serving data from General Component';
errorMessage: any;
status: GeneralTab [];
mode = 'Observable';
constructor (private generalService: GeneralTabService) {
this.status = [];
}
ngOnInit() {
console.log("Started");
this.generalService.getStatus().subscribe(
(status: any) => this.status = status,
(error: any) => this.errorMessage = error
);
console.log(this.status);
}
}
GeneralTab class
export class GeneralTab {
constructor(public recipientId : string,
public recipientName: string,
public recipientFullname: string,
public ouId:String,
public ouName:String,
public institute:number,
public shortName:String,
public status:String)
{
}
}
I see in the console that .do(data => console.log("All:" + JSON.stringify(data))) is getting me JSON data like
{"subjectsConfig":[{"subject":"Client Data Maintenance","preselected":false,"initialDueDate":"2016-11-24","actionConfigs":[{"action":"SEND","recipients":[{"user":{"recipientId":"BD27A4F5923FCA13","recipientName":"ABTABT","recipientFullname":"ABTABT","ouId":"BD27A4F5923FCA13","ouName":"0015",....
Subscribe is not assigning any data to status array and I'm getting blank "status" array. I want this data as an array in status variable for testing purpose. How do I get?
If you log the response like
console.log("Started");
this.generalService.getStatus().subscribe(
(status: any) => this.status = status,
(error: any) => this.errorMessage = error
);
console.log(this.status);
Of course you will see a blank status since the above code(getStatus().subscribe) is async. Meaning you are making a request then you are waiting for the response inside subscribe and when it comes, you will assign status to this.status. While waiting you are already logging(this.status) which is initially blank.
Instead try this and check the log,
console.log("Started");
this.generalService.getStatus().subscribe(
(status: any) => {
this.status = status;
console.log(this.status);
},
(error: any) => this.errorMessage = error
);
Related
I am using JWT Authentication in my project and it is working well.
The issue I am facing is that before using HTTP INTERCEPTOR I was able to get a normal JSON response from the backend (Spring Boot REST API).
But, after using HTTP INTERCEPTOR (for adding AUTHENTICATION header in all HTTP requests) I am not getting JSON response, instead I am getting response as [Object object].
Most important, the backend is giving response in JSON format, I checked it using postman.
auth.interceptor.ts file
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
//get token from auth service
let token: any = this.authService.getToken();
//check if guest user is a first time visitor
if (!token) {
alert('no token present');
return next.handle(request.clone());
}
//add token and header to the request
request = this.addTokenAndHeader(request, token);
//return
return next.handle(request).pipe(
catchError((err: HttpErrorResponse) => {
alert('inside catch and pipe');
//redirect to login page if error code 401 or 403
if (err.status === 401 || err.status === 403) {
alert(err.status);
this.authService.clear();
this.router.navigateByUrl('/access/login');
}
return throwError('Something went wrong.');
})
);
}
//add token to http request
private addTokenAndHeader(request: HttpRequest<any>, token: string) {
alert('inside add token and header method');
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
loader.interceptor.ts
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
this.loaderService.isLoading.next(true);
return next.handle(request).pipe(
finalize(() => {
this.loaderService.isLoading.next(false);
})
);
}
app.module.ts file
#NgModule({
declarations: [AppComponent, NoInternetComponent],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
MaterialModule,
BrowserAnimationsModule,
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: LoaderInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
user.service.ts - where I am calling API URL. Here I was able to get normal JSON response. But, not getting after using HTTP INTERCEPTOR. Most important, the backend is giving response in JSON format, I checked it using postman.
getUserByPhone(phone: any) {
return new Promise((resolve) => {
this.http
.get(this.constants.apiURL + '/user/get/phone/' + phone)
.subscribe((data: any) => {
alert('inside getuserbyphone method');
alert(data);
resolve(data);
});
});
}
Your help will be highly appreciated. Please come forward to help me in this case, if you have any information reagrding the same. Thanks in advance for solving my problem. Really appreciate it.
There are two ways to handle this , and one of those you already tried i.e using 'JSON.stringify'.Although this may not be a bad option considering interceptors will remain in place. But if you can't/don't want to update your application code already written and just wanted to achieve this via interceptor then in that case I believe you need to update your interceptor code to format response data to a JSON format before consuming it in the application.
You should create a separate interceptor ( best practice and is completely optional if you want to do this in same interceptor) just to format the response. Since you have not share it , do you mind checking the typeof the [Object object] in the response, I assume it should be obviously HTTPResponse type.
By default , you should see response data in the 'body' key of the returned data. I have created a quick example and below are the snippet for the interceptor.
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
import { filter, map } from "rxjs/operators";
#Injectable({
providedIn: 'root',
})
export class FormatResponseInterceptor implements HttpInterceptor {
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
filter(event => event instanceof HttpResponse),
map((event: HttpResponse<any>) => event.clone({ body: event.body }))
);
}
}
so as you see the response if event.body and you can directly consume it in your application , as below :
import { SampleService } from './service/sample.service';
export interface CatInterface {
fact: string | '',
length: number | 0;
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
apiData: CatInterface;
constructor(private sampleService: SampleService){}
ngOnInit(){
this.invokeAPI();
}
private invokeAPI(){
this.sampleService.readData().subscribe({
next: (res)=>{
this.apiData = {...res};
},
error: (err)=>{},
complete:()=>{console.log('Service Subscription Completed!');
}
})
}
}
in the above code , I just deconstructed the response object.
So you should first need to check the response object structure and consume it accordingly.
For additional notes, here is my app.component.html code :
<hello name="{{ name }}"></hello>
<p><span style="background-color: yellow"> FACT from the API is : </span> <b> {{apiData?.fact}}</b></p>
<p> <span style="background-color: yellow">LENGTH from the API is : </span><b> {{apiData?.length}}</b></p>
and below is the screen shot for the output :
Hopefully this will help you to solve your problem. Please let me know by providing your feedback so that it will help others as well in future.
I have a provider service contain this code which give me an error on json() method:
getCategorys(){
var headers = new Headers();
headers.append('Content-Type','application/x-www-form-urlencoded');
return this.http.get("http://localhost/Test1/getCity.php?action=getcategory",{headers: headers}).map(res => res.json());
}
but when I run the code it tell me that there is an error:
property 'json' doesn't exist in type 'Response'
What is the issue in this code? why is it an error? What is the solution? (note: that it happened after I install the latest version of ionic).
My TypeScript code where I call the provider is as follows:
ngOnInit(){
this.recipesProvider.getCategorys().subscribe(
data =>{
this.categorys = data.categorys
console.log(this.categorys)
} ,
error =>{
//console.log(error);
}
)
}
And the html code is:
<ion-content padding class="card-background-page">
<ion-list [virtualScroll]="categorys">
<ion-item *virtualItem="let cat" no-lines>
<ion-card (click)="showRecipes(cat.id , cat.name)">
<img class="card-img" [src]="cat.image"/>
<div class="card-title"><span class="card-title-cat">{{cat.name}}</span></div>
<div class="card-subtitle">41 Listings</div>
</ion-card>
</ion-item>
</ion-list>
</ion-content>
those implementation return to me a cities information
it return the value as a json .. in previous 2 months I were coding with no such error .. but after the last update of ionic 3 it then put a red line under json() method .. and if I cut this method from the code it will not return anything .. I want to put another method or syntax to solve this issue(retreive same data from database .. but with another method which don't show to me an error in type Response) and to protect the project validation
this is my provider code which have the error at json() method =>
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Http , Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class RecipesProvider {
constructor(public http: Http , public httpClient : HttpClient) {
console.log('Hello RecipesProvider Provider');
}
getRecipes(idCity: number) {
var headers = new Headers();
headers.append('Content-Type','application/x-www-form-urlencoded');
return this.http.get("http://localhost/Test1/getCity.php?action=getrecipes&idCity="+idCity,{headers: headers}).map(res => res.json());
}
getCategorys(){
var headers = new Headers();
headers.append('Content-Type','application/x-www-form-urlencoded');
return this.http.get("http://localhost/Test1/getCity.php?action=getcategory",{headers: headers}).map(res => res.json());
}
getDetails(idCategoryD : number){
var headers = new Headers();
headers.append('Content-Type','application/x-www-form-urlencoded');
return this.http.get("http://localhost/Test1/getCity.php?action=getDetails&idCategoryD="+idCategoryD ,{headers: headers}).map(res => res.json());
}
}
Thanks for updating your question.
Http module is now deprecated, I suggest to use HttpClient instead.
Easiest fix for your problem is to remove the map operator like so:
return this.http.get("http://localhost/Test1/getCity.php?action=getrecipes&idCity="+idCity,{headers: headers});
And call the method as before:
this.recipesProvider.getCategorys().subscribe(
data => {
this.categorys = data.categorys
console.log(this.categorys)
},
error =>{
//console.log(error);
}
)
More you can have a look into pipeable operators which allows you more control on the data received.
I made an api call and get a response as below, shown on console
My Api Provider:
import { HttpClient, HttpParams, HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
#Injectable()
export class RestapiProvider {
apiUrl = 'http://example.com/api/GetItemByCategory?cat_id=1&merchant_id=1&json=true';
constructor(public http: HttpClient) {
//console.log('Hello RestapiProvider Provider');
}
getItembyCategory() {
return new Promise(resolve => {
this.http.get(this.apiUrl).subscribe(data => {
resolve(data);
console.log(data);
}, err => {
console.log(err);
});
});
}
Console.log(data) shows
[object Object]: {code: 1, details: Object, msg: "success..", request: "{"cat_id":"1","merchant_id":"1","json":"true"}"}
console.log(data)
I need to parse the json data on 'details: Object'
I tried console.log(data.details)
got error:
Unable to get property 'details' of undefined or null reference
UPDATE
I was trying to use map operator as below
this.http.get(this.apiUrl).map(data => data.json()).subscribe(data => {
resolve(data);
console.log(data);
}
Got another error: property json does not exist on type 'Object'
Please let me know how to make it work on ionic 3.
Regards
console.log SS
I manage to go through the 'details' with
data['details'];
and 'item'
data['details']['item'];
Thanks for all the suggestions.
Why don't you try like this:
Import the toPromise operator:
import 'rxjs/add/operator/toPromise';
Then turn your observable into a promise with it's help. No need to map the response to Json as you use the new Angular HttpClient, it is mapped by default.
getItembyCategory() {
return this.http.get(this.apiUrl)
.toPromise()
.then((data: any) => {
console.log('Success', data.details);
return data;
})
.catch(err => {
console.log('Error', err);
return err;
})
}
This way the code is so much cleaner. It is tested and works. If it doesn't then you should check your apiUrl.
Edited
The reason you receive the error property 'details' does not exist on type 'Object' is because you need to give a type to the data object you receive (other than the default Object). I updated the example with this:
(data: any) => {
console.log(data.details);
return data;
}
This is so much better than the ugly square brackets in data['details'] :).
I'm learning Ang2. I ran the Hero tutorial successfully. to practice, I just added a link in the main page to a get a new component. there is a json file with a list of Radio Station. the following are my service, and the Component:
import { Radio } from '../models/radio';
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/Rx';
#Injectable()
export /**
* RadioService
*/
class RadioService {
radios: Radio[] = [];
len: Number = 0;
http: Http;
constructor(http: Http) {
this.http = http;
}
getRadios() {
console.log('Radios Service is being called');
this.http.get('app/mock/FranceRadioJSON.json')
.map((res: Response) => res.json())
.subscribe((rad: Radio[]) => { // Succes
console.log('Radios Service Success');
this.radios = rad;
this.len = rad.length;
}
, err => {// Failure
console.log('Radio Services Failure');
console.error(err);
}
, // complete
() => {
console.log('Get Radios Complete');
console.log('Records NB: ' + this.len);
console.log('radios nb: ' + this.radios.length)
}
)
;
return this.radios;
}
and the Component is:
import { Component, OnInit } from '#angular/core';
import { RouteParams } from '#angular/router-deprecated';
import { Radio } from '../../models/radio';
import { RadioService } from '../../services/radio.service';
#Component({
selector: 'radio'
, templateUrl: 'app/html/radio.component.html'
, providers: [RadioService]
})
export /**
* RadioComponent
*/
class RadioComponent {
radios: Radio[] = [];
constructor(
private radioservice: RadioService) {
}
getRadios() {
this.radios = this.radioservice.getRadios();
console.log('radio component NB: ' + this.radios.length);
}
ngOnInit() {
this.getRadios()
}
}
the problem is the call of service is coming first, and no Radio is return while when the service is called with console.log I see that's is successful and get all records from JSON file. Any help would be greatly appreciated.
You can't get data this way from async calls (which is when you use Promise or Observable). Http returns and Observable. You need to subscribe() and in the callback you pass to subscribe(...) you get the data and can assign it to local properties.
And example how you can solve this:
In getRadios we don't call subscribe() and use map() instead. map() returns an Observable which allows the caller to get the data. subscribe() returns a Subscription and doesn't allow to get the response data, it only allows to cancel the subscription (which is also often handy, but not now ;-) ).
getRadios() {
console.log('Radios Service is being called');
return this.http.get('app/mock/FranceRadioJSON.json')
.map((res: Response) => res.json())
.map((rad: Radio[]) => { // Succes
console.log('Radios Service Success');
this.radios = rad;
this.len = rad.length;
});
}
We subscribe in getRadios because here we want to get the data. When we subscribe, the map() calls in getRadios are executed as well. subscribe() makes the Observable actually do its work:
getRadios() {
this.radios = this.radioservice.getRadios()
.subscribe((radios) => {
this.radios = radios;
} , err => {// Failure
console.log('Radio Services Failure');
console.error(err);
} , // complete
() => {
console.log('Get Radios Complete');
console.log('Records NB: ' + this.len);
console.log('radios nb: ' + this.radios.length)
});
// executed before the HTTP get call to the server
// console.log('radio component NB: ' + this.radios.length);
}
Because data is not returned immediately but only when the callback passed to subscribe(...) is executed you can get errors for bindings like
<div>{{radios.xxx}}</div>
when radios is still null when Angular2 already resolves the bindings in the template. To avoid errors you can use the safe-navigation (Elvis) operator
<div>{{radios?.xxx}}</div>
Angular2 doesn't try to access .xxx while radios is null.
The problem is that you are subscribing to your observable from within your service. Your service should return an Observable and then you Subscribe to it from your component.
So your service should be:
getRadios() : Observable<Radio[]> {
return this.http.get('app/mock/FranceRadioJSON.json')
.map((res: Response) => <Radio[]>res.json())
}
And your component:
getRadios() {
this.radios = this.radioservice.getRadios().subscribe(
r => {
this.radios = r;
}
...
);
}
Hi I am new to Angular and I have been trying to learn Angular 2 so be gentle :).
I have been trying to use WordPress as my data API using WP API plugin. And have so far been able to get posts from WordPress. And below is my code for the data service.
import {Injectable} from "angular2/core";
import {Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Rx';
import {PostInterface} from './data.interface';
import {Headers} from "angular2/http";
import {RequestOptions} from "angular2/http";
#Injectable()
export class DataService{
private _dataURL : string = 'http://localhost/wordpress/?rest_route=/wp/v2/posts';
posts : PostInterface [];
post : PostInterface;
errorMessage : string;
constructor(private http:Http){}
getPosts():Observable<any[]>{
//return this.http.get(this._dataURL).map((res:Response) => res.json());
return this.http.get(this._dataURL)
.map(res=>res.json())
//.do(data => console.log(data)) // eyeball results in the console
.catch(this.handleError);
}
//todo fix search
getPost(filterid:number):Observable<any[]>{
//filterid is the id of a specific post
this._dataURL = this._dataURL + '/' + filterid;
return this.http.get(this._dataURL)
.map(res => res.json())
.catch(this.handleError);
}
private handleError (error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
In the code I get all the post data using the getPosts() method but I also have a getPost() method to fetch specific post.
I was wondering if I could use the JSON data fetched by getPosts() and use it again in getPost() method. Currently what the getPost() does is call the http.get again I do not want to make http.get request again and again.
I want the getPosts() to make one request, fetch the data and store is somewhere so that other methods can use the data and do their specific manipulations.
Thanks
Yes you can firstly fetch your all data and save into one variable or another methods is where you subscribing your data perform for loop and match with your filterId where the process matches store that data into array and implement your manipulation according to need. here is example assuming your data is in array form..
import {Injectable} from "angular2/core";
import {Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Rx';
import {PostInterface} from './data.interface';
import {Headers} from "angular2/http";
import {RequestOptions} from "angular2/http";
#Injectable()
export class DataService{
private _dataURL : string = 'http://localhost/wordpress/?rest_route=/wp/v2/posts';
posts : PostInterface [];
post : PostInterface;
errorMessage : string;
constructor(private http:Http){}
getPosts():Observable<any[]>{
//return this.http.get(this._dataURL).map((res:Response) => res.json());
return this.http.get(this._dataURL)
.map(res=>{
if(res.json()){
return res.json()
}
});
//.do(data => console.log(data)) // eyeball results in the console
.catch(this.handleError);
}
// Method in any file where you want to subscribe your data and wanna fetch specific post //
singlePost: Array<any>= [];
methodName(filterid:number){
service.getPosts()
.subscribe(res=>{
console.log(res) // Here you data whihc is coming from .map i.e getPosts methods using Http
for(let i=0; i< res.length ; i++){ // I am asuming your data is in array from so performing length functionality
if(filterid == res[i].filterid){
this.singlePost = res[i];
break;
}
}
console.log(this.singlePost) // This will return your single Specific POst without using `Http` again and again
})
}
You could try something like that using the do operator to save the data into your service when the getPosts result is received:
#Injectable()
export class DataService{
private _dataURL : string = 'http://localhost/wordpress/?rest_route=/wp/v2/posts';
posts : PostInterface [];
post : PostInterface;
errorMessage : string;
constructor(private http:Http){}
getPosts():Observable<any[]>{
//return this.http.get(this._dataURL).map((res:Response) => res.json());
return this.http.get(this._dataURL)
.map(res=>res.json())
.do(data => this.posts = data) // <--------
.catch(this.handleError);
}
findPostById(id) {
if (this.posts != null) {
return this.posts.find(((element, index, array) {
return (element.id = id);
});
} else {
return null;
}
}
getPost(filterid:number):Observable<any[]>{
var post = findPostById(filterid);
if (post != null) { // <--------
return post;
} else {
this._dataURL = this._dataURL + '/' + filterid;
return this.http.get(this._dataURL)
.map(res => res.json())
.catch(this.handleError);
}
}
Feel free to adapt this code to your needs.