37

I'm trying to convert the following Angular 1 code to Angular 2:

$http.jsonp('https://accounts.google.com/logout');

It needs to be a JSONP request to skip the CORS policy issue.

nunoarruda
  • 2,679
  • 5
  • 26
  • 50

4 Answers4

74

In the latest version of Angular

  1. Import HttpClientModule and HttpClientJsonpModule modules in your app module's definition file

     import {
         HttpClientModule,
         HttpClientJsonpModule
     } from '@angular/common/http';
    
     @NgModule({
     declarations: [
         //... List of components that you need.
     ],
     imports: [
         HttpClientModule,
         HttpClientJsonpModule,
         //...
     ],
     providers: [
         //...
     ],
     bootstrap: [AppComponent]
     })
    
  2. Inject http and map rxjs operator into your service:

     import {Injectable} from '@angular/core';
     import {HttpClient} from '@angular/http';
     import 'rxjs/add/operator/map';
    
     @Injectable()
     export class MegaSuperService {
        constructor(private _http: HttpClient) {}
     }
    
  3. Make JSONP requests in the following way:

     // inside your service
     this._http.jsonp('/api/get', 'callback').map(data => {
     // Do stuff.
     });
    

In Angular version 2 - version 4.3

  1. Import JSONP module in your app module's definition file:

     import {JsonpModule} from '@angular/http';
    
     @NgModule({
     declarations: [
         //... List of components that you need.
     ],
     imports: [
         JsonpModule,
         //...
     ],
     providers: [
         //...
     ],
     bootstrap: [AppComponent]
     })
    
  2. Inject jsonp service and map rxjs operator into your service:

     import {Injectable} from '@angular/core';
     import {Jsonp} from '@angular/http';
     import 'rxjs/add/operator/map';
    
     @Injectable()
     export class MegaSuperService {
        constructor(private _jsonp: Jsonp) {}
     }
    
  3. Make requests using "JSONP_CALLBACK" as a callback property:

     // inside your service
     this._jsonp.get('/api/get?callback=JSONP_CALLBACK').map(data => {
     // Do stuff.
     });
    
Boolean
  • 163
  • 12
EternalLight
  • 1,323
  • 1
  • 11
  • 19
  • 5
    any idea why this "exact same code" can throw a `EXCEPTION: Response with status: 200 Ok for URL: null`, never entering in the `map` function ? – MoshMage Nov 04 '16 at 22:04
  • 1
    I'm getting the follow error: Uncaught ReferenceError: __ng_jsonp____req0_finished is not defined and JSONP injected script did not invoke callback. Any idea? My api call has the jsonp parameter. https://api.instagram.com/v1/users/self/?callback=JSONP_CALLBACK&access_token={myAccesToken} I'm importing jsonp from angular/http and injecting in into the constructor. Also I added to the imports in the app.module.ts file. – Gaston K Feb 10 '17 at 12:07
  • @MoshMage are you seeing that behavior in both IE & Chrome? Chrome is working fine for me, IE is throwing an error behind the scenes yet returning a response 200: OK to the subscription. – nebulae Feb 27 '17 at 17:52
  • @GastonK is the API expecting the callback to be "JSONP_CALLBACK"? angular overwrites this with the __ng_jsonp____req_finished if it sees this exact string. see: https://github.com/angular/angular/blob/13ba2f90b9551e7aa9f14e5a7e5feab3d47a9ee7/modules/%40angular/http/src/backends/jsonp_backend.ts#L74 – nebulae Feb 27 '17 at 17:55
27

In Angular 4.3 and up you should use HttpClientModule because the JsonpModule is deprecated.

  1. Import HttpClientModule and HttpClientJsonpModule into your module.
  2. Inject HttpClient into your service.
  3. Pass the callback key as the second argument for the jsonp method.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
// Import relevant http modules
import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';

import { AppComponent } from './app.component';

import { ExampleService } from './example.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    // Import relevant http modules
    HttpClientModule,
    HttpClientJsonpModule
  ],
  providers: [ExampleService],
  bootstrap: [AppComponent]
})
export class AppModule { }

example.service.ts

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

@Injectable()
export class ExampleService {

  // Inject HttpClient class
  constructor(private http: HttpClient) { }

  getData() {
    const url = "https://archive.org/index.php?output=json&callback=archive";

    // Pass the key for your callback (in this case 'callback')
    // as the second argument to the jsonp method
    return this.http.jsonp(url, 'callback');
  }
}
Courtney Pattison
  • 1,596
  • 2
  • 19
  • 27
  • 1
    Thank you. Just an addition to this answer, we need to replace 'callback' with 'JSONP_CALLBACK'. – Rahul Upadhyay Jan 11 '18 at 22:20
  • @RahulUpadhyay I've updated the answer with your correction. Thank you! – Courtney Pattison Jan 15 '18 at 22:33
  • 1
    As a data point, I found that not only was `&callback=JSONP_CALLBACK` unnecessary, it broke things - but perhaps that was in a different/newer/older version of Angular; anyway, it just "knew" to add and then remove the callback info for some reason. YMMV. – kcrisman Feb 09 '18 at 04:03
  • 1
    @kcrisman Good catch! Looking at [line 1124 of the HttpClient](https://github.com/angular/angular/blob/bf6a416bce99d16130655cd6f833866ea0c4fe6c/packages/common/http/src/client.ts) `JSONP_CALLBACK` is appended to the callback parameter. I'll update my answer now. – Courtney Pattison Mar 29 '18 at 21:32
  • 1
    Now there is a new issue. Cross-Origin Read Blocking (CORB) blocked cross-origin response – alex351 Sep 16 '20 at 22:41
  • This has changed little over time. However, 3rd-parties may have their own name for the callback parameter, that second argument to http.jsonp. I have seen 'callback' or 'c' most commonly. And so in the url in this answer, `&callback=archive` doesn't need to exist. Angular will add that parameter you name to the passed url, such as `&callback=ng_jsonp_callback_0`. The actual callback function should be the success function of the http.jsonp Observable. This extra bit helped me to deal with Pardot form submissions. – workaholic Jul 15 '21 at 09:13
11

If this endpoint is jsonp-compliant, you can use the following. You need to find out the parameter to use to provide the jsonp callback. In the code below, I call it c.

After having registered JSONP_PROVIDERS when calling the bootstrap function:

import {bootstrap} from 'angular2/platform/browser'
import {JSONP_PROVIDERS} from 'angular2/http'
import {AppComponent} from './app.component'

bootstrap(AppComponent, [ JSONP_PROVIDERS ]);

You can then execute your request using an instance of the Jsonp class you injected from constructor:

import {Component} from 'angular2/core';
import {Jsonp} from 'angular2/http';

@Component({
  selector: 'my-app',
  template: `
    <div>
      Result: {{result | json}}
    </div>
  `
})
export class AppComponent {
  constructor(jsonp:Jsonp) {
    var url = 'https://accounts.google.com/logout&c=JSONP_CALLBACK';
    jsonp.request(url, { method: 'Get' })
     .subscribe((res) => {
       (...)
     });
  }
}

See this question for more details:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
0

With Angular 15's standalone component API, use withJsonpSupport():

bootstrapApplication(AppComponent, {
  providers: [provideHttpClient(withJsonpSupport())],
});
Eric Simonton
  • 5,702
  • 2
  • 37
  • 54