Ionic 2: Offline HTTP Get request - http

I am currently loading a list of people randomly taken from the randomuser.me api.
When I turn my internet connection off, I'm simply getting a net::ERR_INTERNET_DISCONNECTED error.
In a "no internet" case I would like to somehow save the http get request and when the connection turns on again, automatically call the saved request. Is that possible ?
Hope I'm clear enough.
Thanks

You can use Network plugin. more info here
for example:
1-create a service to check the connectivity and add this to app.module.ts providers, so you can access it from everywhere:
import { Injectable } from '#angular/core';
import { Platform } from 'ionic-angular';
import { Network } from 'ionic-native';
#Injectable()
export class ConnectivityService {
onDevice: boolean;
constructor(
private platform: Platform
) {
this.onDevice = this.platform.is('cordova');
}
isOnline(): boolean {
if (this.onDevice && Network.type !== 'none') {
return true;
} else {
return navigator.onLine;
}
}
}
2- in your .ts file
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
//service
import { ConnectivityService } from '../../providers/connectivity.service';
#Component({
templateUrl: 'page.html',
})
export class Page {
constructor(
private nav: NavController,
private cs: ConnectivityService
) {}
onCallAPI() {
if (this.cs.isOnline()) {
//do somthing
}
}
}
3- finally if you need to check until your device become online again, you need to add a connectivity listener.

Related

Cant resolve all parameters

Hi I am building a toDo app and i ran into this problem after i completed all of the steps.
error---
Runtime Error
Can't resolve all parameters for TaskListPage: ([object Object], ?).
Stack
import { Component } from '#angular/core';
import { NavController, ItemSliding } from 'ionic-angular';
import {Task} from './task';
import {AngularFire, FirebaseListObservable} from 'angularfire2';
#Component({
selector: 'page-tasklist',
templateUrl: 'tasklist.html'
} )
export class TaskListPage {
tasks: FirebaseListObservable <any[]>;
constructor(public navCtrl: NavController, af: AngularFire) {
this.tasks = af.database.list('/tasks');
}
addItem() {let theNewTask: string = prompt("New Task");
if (theNewTask !== '')
{ this.tasks.push({ title: theNewTask, status: 'open'});
}
}
markAsDone( slidingItem: ItemSliding, task: Task)
{ this.tasks.update(task.$key, {status: 'done'});
slidingItem.close();
}
removeTask(slidingItem: ItemSliding, task: Task)
{ this.tasks.remove(task.$key);
slidingItem.close();
}
}
This is a place where Angular can really improve its error messaging. The offending line is this one:
constructor(public navCtrl: NavController, af: AngularFire) {
You have no access modifier on the af variable, so it is assumed to be a call-time variable (no public, protected or private, so Angular does not know it is supposed to be a class member variable). Since at compile time, what af will be is not known, it results in that warning.
The simple solution is: add an access modifier on the variable. If it is not meant to be shared, private is usually the appropriate modifier.
Also, AngularFire does not look like a valid export from angularfire2. It looks like it should be either AngularFirestore or one of the functionality specific modules - auth, messaging, etc. Also make sure you have set up your AngularFireModule in your app.module, by following the setup directions in their repo.

Make a request for Json data inside a constructor of an Angular 4 home module?

I'm a total newbie into Angular/Typescript web development.
I'm developing a website using ASP.NET Core 2.0 and Angular 4. It needs to fetch some data and present it at the homepage (that would be the home component of the Angular app). I've seen some examples and they suggest doing something like this:
import { Component, Inject } from '#angular/core';
import { Http } from '#angular/http';
#Component({
selector: 'quotes',
templateUrl: './quotes.component.html'
})
export class FetchDataComponent {
public quotes: Quote[];
constructor(http: Http, #Inject('BASE_URL') baseUrl: string) {
http.get(baseUrl + 'api/quotes/recent').subscribe(result => {
this.quotes = result.json() as Quote[];
}, error => console.error(error));
}
}
interface Quote {
text: string;
author: string;
timeStamp: Date;
}
That code works fine when the component is not the first one to be presented when the page is loaded. If I try to fetch data on the home component, the server freaks out and throws all kind of exceptions. First, it throws a TaskCancelledException, and further requests throw:
NodeInvocationException: Prerendering timed out after 30000ms because the boot function in 'ClientApp/dist/main-server' returned a promise that did not resolve or reject.
I'm assuming that I'm doing stuff very wrong, but I haven't seen any other way of doing what I want.
I tried moving the offending code (the http.get request) to a separate function, but now I don't know how am I supposed to call it when the component finished loading.
import { Component, Inject } from '#angular/core';
import { Http } from '#angular/http';
#Component({
selector: 'quotes',
templateUrl: './quotes.component.html'
})
export class FetchDataComponent {
public quotes: Quote[];
private ht: Http;
private url: string;
constructor(http: Http, #Inject('BASE_URL') baseUrl: string) {
this.ht = http;
this.url = baseUrl;
}
fetchQuotes(): void {
this.ht.get(baseUrl + 'api/quotes/recent').subscribe(result => {
this.quotes = result.json() as Quote[];
}, error => console.error(error));
}
}
interface Quote {
text: string;
author: string;
timeStamp: Date;
}
No http event has helped me. I can make everything work using the (click)="" directive, but obviously, I don't want the user to have to click something for the app to work as expected. No other directive seems to work either.
Below is the code I have on the html of the component:
<p class="warning" *ngIf="!quotes" (click)="fetchQuotes()">
<span class="glyphicon glyphicon-warning-sign"></span>
<em>There's nothing to show yet.</em>
</p>
<div *ngIf="quotes">
<ul class="quoteList" *ngFor="let quote of quotes">
<li>
{{ quote.text }}
<small>{{ quote.author }}, {{ quote.timeStamp }}</small>
</li>
</ul>
</div>
So to summarize, I need a way to fetch data for the component that Angular will show by default upon loading the page.
So to summarize, I need a way to fetch data for the component that
Angular will show by default upon loading the page.
The typical way to do this is to implement the Angular OnInit interface, which allows you to do initialization in the ngOnInit() callback method.
import { OnInit } from "#angular/core";
// ...
export class FetchDataComponent implements OnInit {
ngOnInit() {
// do initial data load here
}
}

AngularJs 2 Http CONNECTION ERROR

AngularJs 2 with Webpack.
I am not able to connect to NYT Api.
ALL ENDOPOINTS TESTED AND WORKING PROPERLY
AngularJs 2 in production mode:
enableProdMode();
App:
-1 component
-1 Service
All other components working/displaying properly.
No other services on app yet.
Service returns with error: (in console)
error: "Collection 'topstories' not found"
The Service
import { Injectable } from '#angular/core';
import { Http, Headers, URLSearchParams, Response } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { Observable } from 'rxjs';
#Injectable()
export class NewsService {
private topStoriesUrl: string = `https://api.nytimes.com/svc/topstories/v2/politics.json`;
// private topStoriesUrl: string = `https://newsapi.org/v1/articles`;
// Injecting Http capabilities
constructor( private http: Http ) {}
// for error handling
private handleError(error: any): Promise<any>{
console.error("FromSERVICE:::---:::--> ", error);
return Promise.reject( error.message || error );
}
getNews(): Observable<any> {
let parms: URLSearchParams = new URLSearchParams();
parms.set("api-key", "184db335652341518bea3e4a5db85494");
// parms.set("source", "associated-press");
// parms.set("apiKey", "e4e2aa62a883464a87547e8de4336f61");
return this.http.get( this.topStoriesUrl, { search: parms } )
.map( (res: Response) => res['articles'] )
.catch( this.handleError );
}
}
The Component
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Observable } from 'rxjs';
// service for fetcing news from api
import { NewsService } from '../services/news.service';
#Component({
selector: 'main-news',
templateUrl: '../templates/main-news.component.html'
})
export class MainNewsComponent implements OnInit{
private news: Observable<any>;
constructor(
private router: Router,
private newsService: NewsService
) {}
ngOnInit(): void {
this.newsService.getNews().subscribe( {
next: r => this.news = Observable.of<any[]>(["one"]),
error: err => console.error("From COMPONENT--->", err)
} );
}
}
I have tried this call with both Api from different organizations to get
the same error on the URL resource.
I have tried this same call with said URL with a Ruby script (NET/http) and also directly on the browser address bar, to receive a VALID response on these BOTH cases.
NOT SURE WHY IT FAILS WITH ANGULAR.
HELP!!!
From your console error it looks like the url is not found on server.
The error displayed is returned from server its not angularjs specific error.
Notice 404 not found returned.
So check your url and server again.
Turns out I was missing the:
Access-control-allow-origin
header.
Go figure!
Apparently, it is not added automatically by AngularJs 2.
Thanx

Angular2 http get request with Observables and dynamic url params. How to?

Having this angular2 service taken from official docs with Observable, trying to modify to can pass on fly to the base heroesUrl dynamic parameters like app/heroes/{{country}} and use it like
getHeroes(country) {}
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Hero } from './hero';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class HeroService {
constructor (private http: Http) {}
private heroesUrl = 'app/heroes'; // URL to web API
getHeroes (): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
private handleError (error: any) {
// In a real world app, we might use a remote logging infrastructure
// We'd also dig deeper into the error to get a better message
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
How would I do that?
I think you just need to do following things if I understood your point,
getHeroes(country) {}
export class HeroService {
constructor (private http: Http) {}
private heroesUrl = 'app/heroes'; // URL to web API
getHeroes (country): Observable<Hero[]> { //<-----added parameter
return this.http.get(this.heroesUrl + '/' + country) //<-----changed
.map(this.extractData)
.catch(this.handleError);
}
...
...
}

What is httpinterceptor equivalent in angular2?

In angularjs, we have http interceptor
$httpProvider.interceptors.push('myHttpInterceptor');
with which we can hook into all http calls, and show or hide loading bars, do logging, etc..
What is the equivalent in angular2?
As #Günter pointed it out, there is no way to register interceptors. You need to extend the Http class and put your interception processing around HTTP calls
First you could create a class that extends the Http:
#Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log('request...');
return super.request(url, options).catch(res => {
// do something
});
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
console.log('get...');
return super.get(url, options).catch(res => {
// do something
});
}
}
and register it as described below:
bootstrap(AppComponent, [HTTP_PROVIDERS,
new Provider(Http, {
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new CustomHttp(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
})
]);
The request and requestError kinds could be added before calling the target methods.
For the response one, you need to plug some asynchronous processing into the existing processing chain. This depends on your need but you can use operators (like flatMap) of Observable.
Finally for the responseError one, you need to call the catch operator on the target call. This way you will be notified when an error occurs in the response.
This links could help you:
Handling refresh tokens using rxjs
Angular 2 - How to get Observable.throw globally
update
The new HttpClient module introduced in Angular 4.3.0 supports interceptors https://github.com/angular/angular/compare/4.3.0-rc.0...4.3.0
feat(common): new HttpClient API HttpClient is an evolution of the
existing Angular HTTP API, which exists alongside of it in a separate
package, #angular/common/http. This structure ensures that existing
codebases can slowly migrate to the new API.
The new API improves significantly on the ergonomics and features of
the legacy API. A partial list of new features includes:
Typed, synchronous response body access, including support for JSON body types
JSON is an assumed default and no longer needs to be explicitly parsed
Interceptors allow middleware logic to be inserted into the pipeline
Immutable request/response objects
Progress events for both request upload and response download
Post-request verification & flush based testing framework
original
Angular2 doesn't have (yet) interceptors. You can instead extend Http, XHRBackend, BaseRequestOptions or any of the other involved classes (at least in TypeScript and Dart (don't know about plain JS).
See also
RFC: Http interceptors and transformers
Introduce an interception mechanism
Interceptors in Angular2
Angular2 - set headers for every request
There's an implementation for a Http #angular/core-like service in this repository: https://github.com/voliva/angular2-interceptors
You just declare the provider for that service on bootstrap, adding any interceptors you need, and it will be available for all the components.
import { provideInterceptorService } from 'ng2-interceptors';
#NgModule({
declarations: [
...
],
imports: [
...,
HttpModule
],
providers: [
MyHttpInterceptor,
provideInterceptorService([
MyHttpInterceptor,
/* Add other interceptors here, like "new ServerURLInterceptor()" or
just "ServerURLInterceptor" if it has a provider */
])
],
bootstrap: [AppComponent]
})
DEPRICATED SINCE Angular 4.3 (HttpInterCeptors are Back in 4.3)
You can create your own custom HTTP Class and use rxjs Subject Service to reuse your custom Http Class and implement your behaviors in a custom class.
Implementation of your Custom Http Class with "HttpSubjectService" which contains some rxjs Subjects.
import { Injectable } from '#angular/core';
import { Http, ConnectionBackend, Request, RequestOptions, RequestOptionsArgs, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { HttpSubjectService } from './httpSubject.service';
#Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private httpSubjectService: HttpSubjectService) {
super(backend, defaultOptions);
//Prevent Ajax Request Caching for Internet Explorer
defaultOptions.headers.append("Cache-control", "no-cache");
defaultOptions.headers.append("Cache-control", "no-store");
defaultOptions.headers.append("Pragma", "no-cache");
defaultOptions.headers.append("Expires", "0");
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
//request Start;
this.httpSubjectService.addSpinner();
return super.request(url, options).map(res => {
//Successful Response;
this.httpSubjectService.addNotification(res.json());
return res;
})
.catch((err) => {
//Error Response.
this.httpSubjectService.removeSpinner();
this.httpSubjectService.removeOverlay();
if (err.status === 400 || err.status === 422) {
this.httpSubjectService.addHttp403(err);
return Observable.throw(err);
} else if (err.status === 500) {
this.httpSubjectService.addHttp500(err);
return Observable.throw(err);
} else {
return Observable.empty();
}
})
.finally(() => {
//After the request;
this.httpSubjectService.removeSpinner();
});
}
}
Custom module to register your CustomHttp class - here you overwrite the default Http implementation from Angular with your own CustomHttp Implementation.
import { NgModule, ValueProvider } from '#angular/core';
import { HttpModule, Http, XHRBackend, RequestOptions } from '#angular/http';
//Custom Http
import { HttpSubjectService } from './httpSubject.service';
import { CustomHttp } from './customHttp';
#NgModule({
imports: [ ],
providers: [
HttpSubjectService,
{
provide: Http, useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, httpSubjectService: HttpSubjectService) => {
return new CustomHttp(backend, defaultOptions, httpSubjectService);
},
deps: [XHRBackend, RequestOptions, HttpSubjectService]
}
]
})
export class CustomHttpCoreModule {
constructor() { }
}
now we need the HttpSubjectService Implementation where we can SubScribe to our rxjs Subjects when they get called with the "next" statement.
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class HttpSubjectService {
//https://github.com/ReactiveX/rxjs/blob/master/doc/subject.md
//In our app.component.ts class we will subscribe to this Subjects
public notificationSubject = new Subject();
public http403Subject = new Subject();
public http500Subject = new Subject();
public overlaySubject = new Subject();
public spinnerSubject = new Subject();
constructor() { }
//some Example methods we call in our CustomHttp Class
public addNotification(resultJson: any): void {
this.notificationSubject.next(resultJson);
}
public addHttp403(result: any): void {
this.http403Subject.next(result);
}
public addHttp500(result: any): void {
this.http500Subject.next(result);
}
public removeOverlay(): void {
this.overlaySubject.next(0);
}
public addSpinner(): void {
this.spinnerSubject.next(1);
}
public removeSpinner(): void {
this.spinnerSubject.next(-1);
}
}
to call your custom Implementations we need to Subscribe to the Subjects in the e.g. "app.component.ts".
import { Component } from '#angular/core';
import { HttpSubjectService } from "../HttpInterception/httpSubject.service";
import { Homeservice } from "../HttpServices/home.service";
#Component({
selector: 'app',
templateUrl: './app.component.html',
})
export class AppComponent {
private locals: AppLocalsModel = new AppLocalsModel();
constructor(private httpSubjectService : HttpSubjectService, private homeService : Homeservice) {}
ngOnInit(): void {
this.notifications();
this.httpRedirects();
this.spinner();
this.overlay();
}
public loadServiceData(): void {
this.homeService.getCurrentUsername()
.subscribe(result => {
this.locals.username = result;
});
}
private overlay(): void {
this.httpSubjectService.overlaySubject.subscribe({
next: () => {
console.log("Call Overlay Service");
}
});
}
private spinner(): void {
this.httpSubjectService.spinnerSubject.subscribe({
next: (value: number) => {
console.log("Call Spinner Service");
}
});
}
private notifications(): void {
this.httpSubjectService.notificationSubject.subscribe({
next: (json: any) => {
console.log("Call Notification Service");
}
});
}
private httpRedirects(): void {
this.httpSubjectService.http500Subject.subscribe({
next: (error: any) => {
console.log("Navigate to Error Page");
}
});
this.httpSubjectService.http403Subject.subscribe({
next: (error: any) => {
console.log("Navigate to Not Authorized Page");
}
});
}
}
class AppLocalsModel {
public username : string = "noch nicht abgefragt";
}
SINCE ANGULAR 4.3 you can Use InterCeptors
In Angular 4.3 you have native Interceptors where you can implement your own stuff like a redirect for server error 500
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class SxpHttp500Interceptor implements HttpInterceptor {
constructor(public router: Router) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).do(evt => { }).catch(err => {
if (err["status"]) {
if (err.status === 500) {
this.router.navigate(['/serverError', { fehler: JSON.stringify(err) }]);
}
}
return Observable.throw(err);
});
}
}
you need to Register this in your core module in the providers Array
import { HTTP_INTERCEPTORS } from '#angular/common/http';
import { Router } from '#angular/router';
import { SxpHttp500Interceptor } from "./sxpHttp500.interceptor";
....
providers: [
{
provide: HTTP_INTERCEPTORS, useFactory: (router: Router) => { return new SxpHttp500Interceptor(router) },
multi: true,
deps: [Router]
}
]
With the Angular 4.3.1 release, there's now an interface called HttpInterceptor.
Here's the link to the docs:
https://angular.io/api/common/http/HttpInterceptor
Here's an implementation sample.
This would be the interceptor class implementation.
Is basically written as any other service:
#Injectable()
export class ExceptionsInterceptor implements HttpInterceptor {
constructor(
private logger: Logger,
private exceptionsService: ExceptionsService,
private notificationsService: NotificationsService
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.do((event) => {
// Do nothing here, manage only errors
}, (err: HttpErrorResponse) => {
if (!this.exceptionsService.excludeCodes.includes(err.status)) {
if (!(err.status === 400 && err.error['_validations'])) {
this.logger.error(err);
if (!this.notificationsService.hasNotificationData(err.status)) {
this.notificationsService.addNotification({ text: err.message, type: MessageColorType.error, data: err.status, uid: UniqueIdUtility.generateId() });
}
}
}
});
}
}
Then since you'll treat this like a normal service, you have to add this line inside your app module's providers:
{ provide: HTTP_INTERCEPTORS, useClass: ExceptionsInterceptor, multi: true }
Hope it can help.
Angular 4.3 now supports Http interceptor out-of-the-box.
Check it out how to use them:
https://ryanchenkie.com/angular-authentication-using-the-http-client-and-http-interceptors
I have released interceptor with following node module. We was create this module for our internal purpose finally we released in npm package manager
npm install angular2-resource-and-ajax-interceptor
https://www.npmjs.com/package/angular2-resource-and-ajax-interceptor
As #squadwuschel pointed out, work is underway to get this functionality into #angular/http. This will be in the form of a new HttpClient API.
See https://github.com/angular/angular/pull/17143 for more details and current status.
Angular2 donot support httpinterceptor like angular1
Here is awesome example of use of httpinterceptor in angular2.
https://github.com/NgSculptor/ng2HttpInterceptor
Try Covalent from Teradata, they provides lots of extensions for Angular and Angular Material.
Check HTTP part, it provides the missing http interceptor in Angular and RESTService(similar to restangular).
I have implemented JWT token authentication via Covalent HTTP in my sample, Please check here.
https://github.com/hantsy/angular2-material-sample/blob/master/src/app/core/auth-http-interceptor.ts
Read my development notes for it, Handle token based Authentication via IHttpInterceptor.

Resources