How can i use Ajax


#1

Hello

So I am trying to make an app with Ionic and I just need to use Ajax to get data from an API.
In a seperate project without Ionic i tested my code and it worked just the way i want so i am able to use the Api by using JQuery Ajax.

My Question is how i can use Ajax, since normal jquery works so far but the $.ajax doesn’t and gives lots of errors

I’m sorry i’m new to Ionic so yea i hope someone can help me out


#2

@Ditger Have you looked at Angular HttpClient? Its the official way to make http requests in angular.

You can also use fetch directly, although may be a bit low level, but not much, and is widely supported in the major browsers:

fetch(myUrl, { method: 'GET' }).then(res => console.log(res))

#3

I looked it up but I was confusing, could you help me with the following code that I have written in AJAX

    function EpochToDate(epoch) {
        if (epoch < 10000000000)
            epoch *= 1000; // convert to milliseconds (Epoch is usually expressed in seconds, but Javascript uses Milliseconds)
        var epoch = epoch + (new Date().getTimezoneOffset() * -1); //for timeZone
        return new Date(epoch);
    }

    // https://coinmarketcap.com/api/ -> use the "Ticker (Specific Currency)" endpoint
    $.ajax({
        method: "GET",
        url: "https://api.irail.be/liveboard/?id=BE.NMBS.008892007&format=json",
    })
        .done(function(res) {
            $(".selectedStation").html(res.station);
            $(".station").html(res.departures.departure["29"].station);
            $(".platform").html(res.departures.departure["29"].platform);
            var arriveTime = res.departures.departure["29"].time;
            $(".time").html(EpochToDate(arriveTime));
        });

#4

@Ditger Inject the HttpClient in your service:

my.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class MyService {
  constructor(private http: HttpClient) { }
}

Then create a method in your service to make the http call:

my.service.ts

public myMethod(): Observable<SomeType> {
	const url = 'https://api.irail.be/liveboard/?id=BE.NMBS.008892007&format=json';
	return this.http.get(url, {responseType: 'json'});
}

Where SomeType could be some interface that you create to represent the object:

my.interface.ts

export interface SomeType {
	station: string;
	departures: Departures;
}

export interface Departures {
	departure: Array<Departure>;
}

export interface Departure {
	station: string;
	platform: string;
	time: number;
}

In your component class (ts file) you subscribe to the http call:

my.component.ts

@Component(...)
export class MyComponent {
	...
	public selectedStation: string;
	public station: string;
	public platform: string;
	public time: string;
	
	constructor(private myService: MyService) {}
	
	public makeCall() {
		this.myService.myMethod().subscribe(res => {
			this.selectedStation = res.station;
			this.station = res.departures.departure["29"].station;
			this.platform = res.departures.departure["29"].platform;
            const arriveTime = res.departures.departure["29"].time;
			this.time = EpochToDate(arriveTime);
		});
	}
	...
}

Then, in your component HTML:

my.component.html

<span class"selectedStation">{{ selectedStation }}</span>
<span class"station">{{ station }}</span>
<span class"platform">{{ platform }}</span>
<span class"time">{{ time }}</span>

This covers almost everything. You could make some changes, like using the async pipe if you prefer, but the above should work (maybe with one or another change).

If you don’t understand some of the code above, I advise you to read the Angular docs and see some use cases and demos.


#5

Okay so I tried that, by using ionic g provider and component, I’m still new to this so when I make a name.interface.ts it red underlines the return this.http.get(url, {responseType: 'json'});

I’m a bit lost on how I can do all that, I’m sorry for all these questions, just trying to learn


#6

What shows in the error? Some type wrong or not found?

The line return this.http.get(url, {responseType: 'json'}); should be inside your service, not in the file with your interfaces.

If it’s about the type Object not being able to be assigned to SomeType you can force the type:

my.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

import { SomeType } from './my-interface.ts';

@Injectable()
export class MyService {
	constructor(private http: HttpClient) { }

	public myMethod(): Observable<SomeType> {
		const url = 'https://api.irail.be/liveboard/?id=BE.NMBS.008892007&format=json';
		return this.http.get(url, {responseType: 'json'}).map(obj => <SomeType>obj);
	}
}

(I’m not writing this code in an IDE/Editor, so it could be something wrong with the code, but in general all or the most part of it should work.)


#7

So the error I get now is:
Type 'Observable<Object>' is not assignable to type 'Observable<SomeType>'. Type 'Object' is not assignable to type 'SomeType'. The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead? Property 'station' is missing in type 'Object'.

This is my service.ts which has been generated in the providers folder

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import {Observable} from "rxjs/Observable";
import {SomeType} from "./i-rail-data-service.interface";

/*
  Generated class for the IRailDataServiceProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class IRailDataServiceProvider {

  constructor(public http: HttpClient) {}

  public dataUse(): Observable<SomeType> {
      const url = 'https://api.irail.be/liveboard/?id=BE.NMBS.008892007&format=json';
      return this.http.get(url, {responseType: 'json'});
  }

}

This is the .ts in the components folder

import { Component } from '@angular/core';
import {IRailDataServiceProvider} from "../../providers/i-rail-data-service/i-rail-data-service";

/**
 * Generated class for the IRailDataServiceComponent component.
 *
 * See https://angular.io/api/core/Component for more info on Angular
 * Components.
 */
@Component({
    selector: 'i-rail-data-service',
    templateUrl: 'i-rail-data-service.html'
})
export class IRailDataServiceComponent {

    public selectedStation: string;
    public station: string;
    public platform: string;
    public time: string;

    constructor(private IRailDataService: IRailDataServiceProvider) {}

    public makeCall() {
        this.IRailDataService.dataUse().subscribe(res => {
            this.selectedStation = res.station;
            this.station = res.departures.departure["29"].station;
            this.platform = res.departures.departure["29"].platform;
            this.time = res.departures.departure["29"].time;
        });
    }
}

And the .interface.ts also in the providers folder

export interface SomeType {
    station: string;
    departures: Departures;
}

export interface Departures {
    departure: Array<Departure>;
}

export interface Departure {
    station: string;
    platform: string;
    time: number;
}

#8

@Ditger The solution to your error is in my previous post, take a better look at the line:

return this.http.get(url, {responseType: 'json'}).map(obj => <SomeType>obj);

The Angular HttpClient returns a type Object. Typescript don’t let you assign an Object to a subclass (because not all object of a given superclass type is an object of a given subclass type).

I would prefer that angular returned a generic type instead of Object tough.

The solution is just to make typescript see the object as having the specific type that is returned in the http call:

.map((unknownType: Object) => {
	const knownType = <T>unknownType;
	return knownType;
})

In your specific case:

.map(res => <SomeType>res);

I advise you to read some docs and test some typescript simple demos to have a better base to build your project, otherwise you will run into similar problems frequently.

Update

It seems angular provides a way to use generics, angular http methods are overloaded.

It’s better to call like:

@Injectable()
export class MyService {
	constructor(private http: HttpClient) { }

	public myMethod(): Observable<SomeType> {
		const url = 'https://api.irail.be/liveboard/?id=BE.NMBS.008892007&format=json';
		return this.http.get<SomeType>(url, {responseType: 'json'});
	}
}

#9

Nope.

It returns of type Observable<T> and all operators following will do the same

But maybe that is what u actually meant


#10

I meant Observable<Object> instead of Observable<any> when the responseType is json, if you don’t specify the type:

this.http.get(url, {responseType: 'json'}).subscribe(res => ...); // type is Object, so you can't assign it [directly] to a subclass of Object

this.http.get<SomeType>(url, {responseType: 'json'}).subscribe(res => ...); // type of res is SomeType

If you look at the definition in client.d.ts:

/**
 * Construct a GET request which interprets the body as JSON and returns it.
 *
 * @return an `Observable` of the body as an `Object`.
 */
get(url: string, options?: {
	headers?: HttpHeaders | {
		[header: string]: string | string[];
	};
	observe?: 'body';
	params?: HttpParams | {
		[param: string]: string | string[];
	};
	reportProgress?: boolean;
	responseType?: 'json';
	withCredentials?: boolean;
}): Observable<Object>;
/**
 * Construct a GET request which interprets the body as JSON and returns it.
 *
 * @return an `Observable` of the body as type `T`.
 */
get<T>(url: string, options?: {
	headers?: HttpHeaders | {
		[header: string]: string | string[];
	};
	observe?: 'body';
	params?: HttpParams | {
		[param: string]: string | string[];
	};
	reportProgress?: boolean;
	responseType?: 'json';
	withCredentials?: boolean;
}): Observable<T>;

If you use the first you will see the error that he was seeing.


#11

I’ll try what you said, it is at this moment very confusing to know how to exactly do all of this, but thanks in advance


#12

U may have two challenges: using typescript (anything in between < and >) and understanding httpclient+observables.

I suggest to ditch the typescript and learn the latter first :slight_smile:

That way it wont be as frustrating…I would think


#13

I wrote the front end of the app that I had in mind, all the pages work the way they are supposed to in the end, it’s for a project for school and I’m still stuck on how I can use this, so how can I use it properly since I’m very confused, sorry again for all the questions


#14

Okay so I tried the following

I made a provider using: ionic g provider test

in that test.ts this is the following code

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map'
/*
  Generated class for the TestProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/
@Injectable()
export class TestProvider {

  constructor(public http: Http) {
    console.log('Hello TestProvider Provider');
  }

  getRemoteData() {
    this.http.get("https://api.irail.be/liveboard/?id=BE.NMBS.008892007&format=json").map(res=>res.json()).subscribe(data => {
      console.log(data)
    });
  }
}

My app.module.ts

import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';

import { AboutPage } from '../pages/about/about';
import { ContactPage } from '../pages/contact/contact';
import { HomePage } from '../pages/home/home';
import { TabsPage } from '../pages/tabs/tabs';

import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { TestProvider } from '../providers/test/test';
import {HttpModule} from "@angular/http";

@NgModule({
  declarations: [
    MyApp,
    AboutPage,
    ContactPage,
    HomePage,
    TabsPage
  ],
  imports: [
    BrowserModule,
      HttpModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    AboutPage,
    ContactPage,
    HomePage,
    TabsPage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    TestProvider
  ]
})
export class AppModule {}

And the home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { TestProvider } from '../../providers/test/test';
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController, public testService: TestProvider) {

  }

  ionViewDidLoad() {
    this.testService.getRemoteData();
  }
}

It all prints in the console so that’s a huge step forward :slight_smile:
but i’m wondering how I can display it on the home page

Is this the right way? I watched a couple of videos and I hope someone can still help me


#15

Put the subscribe in home.ts in ionViewDidEnter (never in a provider) and let the provider return only an observable (so everything except the subscribe pattern)

Then use angular template binding to display

Google:angular template syntax

You r getting pretty close!

If u want fast help, put your stuff in stackblitz.com and share the fork?


#16

I tried to put the subscribe , this is the link to the project
The project

Thanks a lot in advance


#17

a bit of cheating on typescript…

and future better to use lettable operators…


#18

Thank you! for everything, I learned so much and thanks a lot for all the help and patience :slight_smile: have a great day :slight_smile:

EDIT: why can’t I use .map(data => data['stationinfo']) in the getRemoteData() in the test.ts?


#19

u need to follow the structure of the data. The map I included flattens slowly the data. The resulting observable should AT ANY COST emit an array, otherwise the iterator in the HTML will fail miserably.

And I am not really into deep diving in Belgian railroad information. So you have to deal with it yourself. Best is to remove the HTML part and use console.log to see if you are doing the proper thing


#20

That’s was what I did, and it seems like I want to use something that isn’t in the departure array, so I looked up some info on how I can use more than one array in the ngfor loop I came up with this, but I’ve not really an idea where I went wrong
More than one array test