I am trying to use Ionic2 and I made a service to fetch a local stored Json.
import {Injectable} from 'angular2/core';
import {Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/map';
#Injectable()
export class Page1Service {
public constructor(private _http: Http) {}
public GetItems() {
return this._http.get('/app/Ressources/Items.json').map((response: Response) => response.json().data);
}
public PrintJson():boolean {
var myresult;
this.GetItems().subscribe((result) => {
myresult = result;
console.log(result);
});
}
I also a made PrintJson() method that just print the json for test purpose.I got the error:
GET http://localhost:8100/app/Ressources/slides.json 404 (Not Found)
I don't get why. And I can't find an easy and uptodate tutorial. Or should I use fetch()?
First copy your json to the following dir(you can create the folder "data"):
[appname]/www/data/data.json
Type in the following command in your console:
ionic g provider JsonData
It should create a provider for you.Go to that page and enter the following in load() function:
load() {
if (this.data) {
// already loaded data
return Promise.resolve(this.data);
}
// don't have the data yet
return new Promise(resolve => {
// We're using Angular Http provider to request the data,
// then on the response it'll map the JSON data to a parsed JS object.
// Next we process the data and resolve the promise with the new data.
this.http.get('data/data.json').subscribe(res => {
// we've got back the raw data, now generate the core schedule data
// and save the data for later reference
this.data = res.json();
resolve(this.data);
console.log(this.data);
});
});
}
I usually create an Observable wrapped around the api-call like this:
public GetItems() {
return Observable.create(observer => {
this._http.get('/app/Ressources/Items.json').map(res =>res.json()).subscribe(data=>{
observer.next(data)
observer.complete();
});
});
}
Then I have to subscribe on that method in order to get the results and do something with it. (You could be to delegate the result to a list in the GUI)
GetItems().subscribe(data=>{
myResult = data;
});
EDIT: It might help to put this in the class as well
export class MyClass{
static get parameters(){
return [[Http]];
}
}
Just try to get the response.json() rather than response.json().data in GetItems() method
The issue is because of different paths of json files in local browser(computer) and device (android). Create data folder inside the src\assets folder. Move your json file into that.
When we run ionic serve, it will move that folder (with file) into www\assets folder. Then do following things:
Import Platform service of ionic2
import { Platform } from 'ionic-angular';
Inject Platform Service.
constructor(private http: Http, private platform: Platform ) { }
Use Platform Service.
public getItems() {
var url = 'assets/data/Items.json';
if (this.platform.is('cordova') && this.platform.is('android')) {
url = "/android_asset/www/" + url;
}
return this.http.get(url)
.map((res) => {
return res.json()
});
}
Related
I am writing an Angular front end for an API that occasionally serves Infinity and -Infinity (as bare words) in the JSON response object. This is of course not compliant with the spec, but is handled a few different JSON libraries, albeit optionally. I have an Angular service in place that can successfully retrieve and handle any retrieved entity that does not have these non-conforming values. Additionally, I have managed to get an HttpInterceptor in place which just logs when events trickle through, just to be sure I have it connected properly.
The issue that I am facing is that the HttpInterceptor seems to allow me to do one of two things:
Catch/mutate the request before it is sent to the API, or
Catch/mutate the request after it comes back from the API, and also after it is parsed.
What I would like to do is very similar to this question for native javascript, but I have not been able to determine if it is possible to tie into the replacer function of JSON.parse in the Angular Observable pipe (I think that if tying into that is possible it would solve my issue).
I have also found this question for Angular which is close, but they appear to have been able to handle changing the response to something other than the bare-words, which I don't have the liberty of doing.
This is the current implementation of my HttpInterceptor, note that it does not actually make any changes to the body. When retrieving an entity without these bare-word values, it logs to the console and all is well. When retrieving an entity with any of these bare-word values, an error is thrown before the HERE line is hit.
function replaceInfinity(body: string): string {
// Do something
return body;
}
#Injectable()
export class JsonInfinityTranslator implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
map((event) => {
if (event instanceof HttpResponse) {
console.log("HERE");
return event.clone({body: replaceInfinity(event.body)});
} else {
return event;
}
})
);
}
}
TL;DR: Is there a way to mutate the body text of the returned response before the Angular built in JSON deserialization?
I was able to figure out how to achieve this, and it came down to:
Modifying the request to return as text instead of json
Catch the text response and replace the bare word symbols with specific string flags
Parse the text into an object using JSON.parse, providing a reviver function to replace the specific string flags with the javascript version of +/-Infinity and NaN
Here's the Angular HttpInterceptor I came up with:
import {Injectable} from '#angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '#angular/common/http';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
#Injectable()
export class JsonBareWordNumericSymbolTranslator implements HttpInterceptor {
private static infinityFlag = '__INFINITY_FLAG__';
private static negInfinityFlag = '__NEG_INFINITY_FLAG__';
private static nanFlag = '__NAN_FLAG__';
private static replaceBareWordSymbolsWithFlags(body: string): string {
const infinityBareWordPattern = /(": )Infinity(,?)/;
const negInfinityBareWordPattern = /(": )-Infinity(,?)/;
const nanBareWordPattern = /(": )NaN(,?)/;
return body
.replace(infinityBareWordPattern, `$1"${this.infinityFlag}"$2`)
.replace(negInfinityBareWordPattern, `$1"${this.negInfinityFlag}"$2`)
.replace(nanBareWordPattern, `$1"${this.nanFlag}"$2`);
}
private static translateJsonWithFlags(substitutedBody: string): any {
return JSON.parse(substitutedBody, (key: string, value: string) => {
if (value === this.infinityFlag) {
return Infinity;
} else if (value === this.negInfinityFlag) {
return -Infinity;
} else if (value === this.nanFlag) {
return NaN;
} else {
return value;
}
});
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.responseType !== 'json') {
// Do not modify requests with response types other than json
return next.handle(req);
}
return next.handle(req.clone({responseType: 'text'})).pipe(
map((event) => {
if (!(event instanceof HttpResponse)) {
return event;
}
const substitutedBody = JsonBareWordNumericSymbolTranslator.replaceBareWordSymbolsWithFlags(event.body);
const parsedJson = JsonBareWordNumericSymbolTranslator.translateJsonWithFlags(substitutedBody);
return event.clone({body: parsedJson});
})
);
}
}
I am not sure what I am doing wrong here.
I am trying to use the checkout facility for stripe using this documentation: https://stripe.com/docs/payments/checkout/accept-a-payment
I have configured my API to just return the checkoutid as a string.
The Angular service just calls the controller. When I run my code I actually get a nice 200 response and I can see the checkout id in the response body, but Angular throws an error:
SyntaxError: Unexpected token c in JSON at position 0 at JSON.parse () at XMLHttpRequest.onLoad (https://127.0.0.1:4200/vendor.js:18780:51) at ZoneDelegate.invokeTask
The service looks like this:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { environment } from '#environments/environment';
#Injectable({
providedIn: 'root',
})
export class StripeService {
private endpoint: string = 'stripe';
constructor(private http: HttpClient) {}
checkout(priceId: string) {
return this.http
.get<string>(`${environment.apiUrl}/${this.endpoint}/${priceId}`)
.pipe(
map((response) => {
console.log(response);
return response;
})
);
}
}
and I am invoking it like this:
this.stripeService
.checkout(this.button.priceId)
.subscribe((checkoutId: string) => {
console.log(checkoutId);
// this.stripe
// .redirectToCheckout({
// sessionId: checkoutId,
// })
// .then(function (result) {
// // If `redirectToCheckout` fails due to a browser or network
// // error, display the localized error message to your customer
// // using `result.error.message`.
// });
});
If I look in the network tab I can see this:
But the console actually shows this:
Does anyone have a scooby why?
Probably the response is a string and you haven't specified the response type. Try the following
this.http.get(
`${environment.apiUrl}/${this.endpoint}/${priceId}`,
{ responseType: 'text' }
)
Default response type is json.
It happened to me when my API return doesent match with my deserializable object on Angular. At first, try to check your returns entities
I have a scaffolding of folders and json files to mock an API's paths. When I run npm run start:mock, LocalMockInterceptor gets provisioned and e.g. replaces a call to host/A/B/C by an http call getting locally Folder A/Folder B/C.json. The JSON files get produced by a separate script which is out of scope here. I cannot make use of "import" as many tutorials show because I need a generic solution as the API i am mocking will evolve over time (and so will this scaffolding of folders and files).
/**
* The idea is to only build this into the bundle if we specify so (e.g. on TeamCity, on your localhost), where you don't want to rely
* on external resources for development
* No conditionals in the code bundle! No configuration files or database dependency.
*/
import {
HttpInterceptor,
HttpResponse,
HttpHandler,
HttpRequest,
HttpEvent,
HttpClient,
HttpHeaders
} from '#angular/common/http';
import { Injectable, Injector } from '#angular/core';
import { Observable, of } from 'rxjs';
import { ErrorService } from './error.service';
const devAssetsFolder = 'assets';
#Injectable()
export class LocalMockInterceptor implements HttpInterceptor {
constructor(
private errorService: ErrorService,
private injector: Injector,
private http: HttpClient
) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (request.url.endsWith('.json')) return next.handle(request);
console.log(
` >>> Mock Interceptor >>> ${request.url} has been intercepted`
);
const path = `${devAssetsFolder}${request.url}.json`;
var promise = this.getJSON(path).toPromise();
const jsonheaders = new HttpHeaders();
jsonheaders.set('Content-Type', 'application/json');
let json2;
promise
.then(json => {
console.log(json);
json2 = json;
})
.catch(error => console.log(error));
Promise.all([promise]);
console.log(json2);
return of(
new HttpResponse({ status: 200, body: json2, headers: jsonheaders })
);
}
private getJSON(jsonPath: string): Observable<any> {
return this.http.get(jsonPath);
}
}
The first conditional is to avoid infinite loops since I am sending HTTP requests in my interceptor
Getting the path to the JSON file based on the URL is quite natural
It seemed to me that I have to convert the JSON Observable into a promise so that I can wait for it to complete before rewrapping that json into the returned Observable. When debugging however, it seems Promise.all is not waiting for the promise to complete (json2 is undefined on the next line), and I end up sending an empty http body back...
How to fix this rxjs promise ?
Is inner HTTP calls my only option ?
Is there a way not to rely on promises ? Can you think of a better way to achieve this ?
Did you try just modifying the target URL in your interceptor ? You want to make an API call that return some JSON but instead of calling a dynamic API, you just want to call you static server so it can return predefined JSON.
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const fakeUrl = `${devAssetsFolder}${request.url}.json`;
const fakeRequest = request.clone({url: fakeUrl});
return next.handle(request);
}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (request.url.endsWith('.json')) return next.handle(request);
console.log(
` >>> Mock Interceptor >>> ${request.url} has been intercepted`
);
const path = `${devAssetsFolder}${request.url}.json`;
return this.getJSON(path).pipe(map(result => {
const jsonheaders = new HttpHeaders({ 'Content-Type': 'application/json' });
return
new HttpResponse({ status: 200, body: result, headers: jsonheaders });
}), // you can also add catchError here
);
}
In intercept method you can return an observable. So your getJSON method returns an observable, we added pipe a map function which maps the result to new http response. If your response already has the right headers you don't even need the pipe and map functions you can just do this :
return this.getJSON(path); // it's an observable, so it's OK.
I'm setting up a service and i want to use a json file with mock data to start with. However i get a TypeError: req.url.toLowerCase is not a function when i use that service with the mock data, how can i resolve this error?
Service:
import mockCompetitions from '../../mocks/competitions-mock.json';
export class CompetitionsService {
constructor(protected http: HttpClient) { }
getCompetitions() {
console.log(mockCompetitions);
return this.http.get(mockCompetitions);
}
}
Component:
competitions: any = [];
constructor(private competitionsService: CompetitionsService) {}
ngOnInit(){
this.getCompetitions();
}
getCompetitions(){
this.competitionsService.getCompetitions().subscribe(data => {
this.competitions = data;
console.log(this.competitions);
}, err => console.error(err), () => console.log('Finished Loading...'));
}
I expect a list of names to be printed out on the page from the json file.
If you want to use httpclient to read local json file, put the json file in assets folder as name-of-the-file.json and make the http request by using assets folder as url. It is important that you put it in the assets folder, so that Angular can find it. So your code should look something like this:
export class CompetitionsService {
constructor(protected http: HttpClient) { }
getCompetitions() {
return this.http.get('./assets/name-of-the-file.json');
}
}
So no need to import the file.
For using json file as a data provider you can use import and require.
import data = require('your_file.json')
console.log("data : ", JSON.stringify(data));
You are using the JSON file as an url to your http.get().
If you want to test your service using mock data, I would recommend some HTTP mocking website, like mocky. Put your JSON file there and use the URL that the site generates for you in your http.get(). You won't have to change anything except that in your code.
I am trying to read a local JSON file in Ionic 3. I have saved the JSON file in assets folder as csvjson.json
I call the following function inside one of the services.
getProducts() {
console.log('Inside getProducts')
return this.http.get(this.apiHost)
.map((response: Response) => {
console.log(response);
return response.json();
});
}
and then store the result in
myArray = this.databaseprovider.getProducts();
console.log("Returned from getProducts:" + myArray.length);
However I get the output as
Returned from getProducts:undefined
Can you pls suggest where I am going wrong?
Put the <file-name>.json file in assets folder and change the request to following,
public getProducts() {
return new Promise((resolve, reject) => {
this._http.get("assets/<file-name>.json")
.map((response: Response) => {
console.log(response);
resolve(response.json());
});
});
}
Component file
this.databaseprovider.getProducts().then((result)=>{
myArray = result;
});
console.log("Returned from getProducts:" + myArray.length);
The easiest way is to use fetch() function like that:
readJsonData(){
fetch("../../assets/data/Parameters.json").then(res=>res.json()).then(json=>{
console.log("OUTPUT: ", json);
//DO YOUR STAFF
});
}```
When you call it in your Typescript File of your Page for example called yourPage.ts in the yourPage folder you can access the local JSON File by importing it:
yourPage.ts:
import * as JSONdata from "../../assets/csvjson.json" //You can name 'JSONdata' as you want
To call it:
getProducts() {
console.log(JSONdata);
}
import { HttpClient } from '#angular/common/http';
constructor(private http: HttpClient) { }
this.http.get("assets.data.json").subscribe((data:any)=>{
console.log(data);
});