Existe-t-il actuellement un moyen dans Angular 2 d’obtenir la progression (c’est-à-dire le pourcentage effectué) d’un appel ajax, en utilisant le module angular2/http?
J'utilise le code suivant pour effectuer mes appels HTTP:
let body = JSON.stringify(params);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(url, body, options)
.timeout(10000, new Error('Timeout exceeded during login'))
.toPromise()
.then((res) => {
...
}).catch((err) => {
...
});
L'objectif est d'écrire un système de synchronisation. La publication renverra beaucoup de données, et je veux donner à l'utilisateur une indication sur la durée de la synchronisation.
Actuellement (à partir de la version 4.3.0, lors de l'utilisation de la nouvelle HttpClient
à partir de @ngular/common/http
), Angular permet d'écouter les progrès réalisés immédiatement. Il vous suffit de créer un objet HTTPRequest comme ci-dessous:
import { HttpRequest } from '@angular/common/http';
...
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true,
});
Et lorsque vous vous abonnez à pour demander, vous serez abonné à chaque événement de progression:
http.request(req).subscribe(event => {
// Via this API, you get access to the raw event stream.
// Look for upload progress events.
if (event.type === HttpEventType.UploadProgress) {
// This is an upload progress event. Compute and show the % done:
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`File is ${percentDone}% uploaded.`);
} else if (event instanceof HttpResponse) {
console.log('File is completely uploaded!');
}
});
Plus d'infos ici .
Vous pouvez exploiter l'événement onprogress
fourni par XHR (voir plunkr: http://plnkr.co/edit/8MDO2GsCGiOJd2y2XbQk?p=preview ).
Cela permet d’obtenir des indications sur la progression du téléchargement. Cela n’est pas pris en charge immédiatement par Angular2, mais vous pouvez le brancher en étendant la classe BrowserXhr
:
@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor(private service:ProgressService) {}
build(): any {
let xhr = super.build();
xhr.onprogress = (event) => {
service.progressEventObservable.next(event);
};
return <any>(xhr);
}
}
et remplacez le fournisseur BrowserXhr
par le paramètre étendu:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
Voir cette question pour plus de détails:
Lorsque vous créez http cals dans angular2, il renvoie un observable de type Response, cette réponse est créée dans la classe appelée XHRConnection où toute la magie se produit.
XHRConnection génère la réponse en écoutant événement de chargement de XMLHttpRequest, ce qui signifie qu'il renverra une réponse à la fin de la demande.
Maintenant pour pouvoir modifier ce comportement, nous devons faire en sorte que notre classe de connexion écoute l'événement progrès.
Nous avons donc besoin de créer une classe de connexion personnalisée pour gérer la réponse comme bon nous semble.
Je l’ai fait de cette façon Prenez note que mon API php renvoie des réponses multiples en une seule requête et que ces réponses sont des chaînes simples.
my_backend.ts
import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Observable";
import {Observer} from "rxjs/Observer";
import {Connection,ConnectionBackend} from "angular2/src/http/interfaces";
import {ReadyState, RequestMethod, ResponseType} from "angular2/src/http/enums";
import {ResponseOptions} from "angular2/src/http/base_response_options";
import {Request} from "angular2/src/http/static_request";
import {Response} from "angular2/src/http/static_response";
import {BrowserXhr} from "angular2/src/http/backends/browser_xhr";
import {Headers} from 'angular2/src/http/headers';
import {isPresent} from 'angular2/src/facade/lang';
import {getResponseURL, isSuccess} from "angular2/src/http/http_utils"
export class MyConnection implements Connection {
readyState: ReadyState;
request: Request;
response: Observable<Response>;
constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
this.request = req;
this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
let _xhr: XMLHttpRequest = browserXHR.build();
_xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
// save the responses in array
var buffer :string[] = [];
// load event handler
let onLoad = () => {
let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
//_xhr.respons 1 = "Loading data!"
//_xhr.respons 2 = "Loading data!Ready To Receive Orders."
// we need to fix this proble
// check if the current response text contains the previous then subtract
// NOTE: I think there is better approach to solve this problem.
buffer.Push(body);
if(buffer.length>1){
body = buffer[buffer.length-1].replace(buffer[buffer.length-2],'');
}
let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
let url = getResponseURL(_xhr);
let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
let state:number = _xhr.readyState;
if (status === 0) {
status = body ? 200 : 0;
}
var responseOptions = new ResponseOptions({ body, status, headers, url });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
let response = new Response(responseOptions);
//check for the state if not 4 then don't complete the observer
if(state !== 4){
//this will return stream of responses
responseObserver.next(response);
return;
}
else{
responseObserver.complete();
return;
}
responseObserver.error(response);
};
// error event handler
let onError = (err: any) => {
var responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
responseObserver.error(new Response(responseOptions));
};
if (isPresent(req.headers)) {
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
}
_xhr.addEventListener('progress', onLoad);
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.send(this.request.text());
return () => {
_xhr.removeEventListener('progress', onLoad);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
};
});
}
}
@Injectable()
export class MyBackend implements ConnectionBackend {
constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
createConnection(request: Request):MyConnection {
return new MyConnection(request, this._browserXHR, this._baseResponseOptions);
}
}
Et dans le app.component.ts
import {Component, provide} from 'angular2/core';
import {HTTP_PROVIDERS,XHRBackend} from 'angular2/http';
import {MyBackend} from './my_backend';
@Component({
selector: 'my-app',
providers: [
HTTP_PROVIDERS,
MyBackend,
provide(XHRBackend, {useExisting:MyBackend})
]
.
.
.
_ {Maintenant appeler http.get retournera une liste de réponses.
Je recommande vivement d'utiliser cette
https://www.npmjs.com/package/angular-progress-http
sinon, jouer avec xhr vous fera rater des cookies de sessions et autres
en plus ça va être plus portable et beaucoup plus facile à mettre en œuvre