2016-05-27 10 views

risposta

16

Sì, è necessario gestire che per ogni Vista:

  • si può avere un servizio per la richiesta HTTP che restituirà un osservabile
  • nel componente si avrà uno stato di caricamento
  • È necessario impostare lo stato di caricamento su true prima di richiedere i dati dal server , quindi impostarlo su false quando viene eseguito il recupero dei dati.
  • Nel modello utilizzare il ngIf per nascondere/mostrare carico o il contenuto

    Es:

Il Servizio:

@Injectable() 
export class DataService { 
    constructor(private http: Http) { } 

    getData() { 
     return this.http.get('http://jsonplaceholder.typicode.com/posts/2'); 
    } 
} 

Il Componente:

@Component({ 
    selector: 'my-app', 
    template : ` 
    <div *ngIf="loading == true" class="loader">Loading..</div> 
    <div *ngIf="loading == false">Content.... a lot of content <br> more content</div>` 
}) 
export class App { 
    loading: boolean; 

    constructor(private dataService: DataService) { } 

    ngOnInit() { 
    // Start loading Data from the Server 
    this.loading = true; 

    this.dataService.getData().delay(1500).subscribe( 
     requestData => { 
     // Data loading is Done 
     this.loading = false; 

     console.log('AppComponent', requestData); 
     } 
    } 
} 

Un esempio di lavoro può essere trovato qui: http://plnkr.co/edit/HDEDDLOeiHEDd7VQaev5?p=preview

+0

facile e semplice. grande. ty – angryip

17

Un modo è quello di scrivere un intercettore per Angular2 Http. Creando la tua istanza http puoi scambiarla quando stai facendo il bootstrap dell'applicazione usando il metodo "fornire". Una volta fatto questo, è possibile creare un servizio PubSub per pubblicare e sottoscrivere questi eventi dall'intercettore Http ed emettere eventi prima e dopo ogni richiesta effettuata.

Un esempio dal vivo può essere visto su Plunker

Interceptor:

import {Injectable} from 'angular2/core'; 
import {HTTP_PROVIDERS, Http, Request, RequestOptionsArgs, Response, XHRBackend, RequestOptions, ConnectionBackend, Headers} from 'angular2/http'; 
import 'rxjs/Rx'; 
import {PubSubService} from './pubsubService'; 

@Injectable() 
export class CustomHttp extends Http { 
    _pubsub: PubSubService 
    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, pubsub: PubSubService) { 
     super(backend, defaultOptions); 
     this._pubsub = pubsub; 
    } 

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> { 
     return this.intercept(super.request(url, options)); 
    } 

    get(url: string, options?: RequestOptionsArgs): Observable<Response> { 
     return this.intercept(super.get(url,options)); 
    } 

    post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> { 
     return this.intercept(super.post(url, body, this.getRequestOptionArgs(options))); 
    } 

    put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> { 
     return this.intercept(super.put(url, body, this.getRequestOptionArgs(options))); 
    } 

    delete(url: string, options?: RequestOptionsArgs): Observable<Response> { 
     return this.intercept(super.delete(url, options)); 
    } 

    getRequestOptionArgs(options?: RequestOptionsArgs) : RequestOptionsArgs { 
     if (options == null) { 
      options = new RequestOptions(); 
     } 
     if (options.headers == null) { 
      options.headers = new Headers(); 
     } 
     options.headers.append('Content-Type', 'application/json'); 
     return options; 
    } 

    intercept(observable: Observable<Response>): Observable<Response> { 
     this._pubsub.beforeRequest.emit("beforeRequestEvent"); 
     //this will force the call to be made immediately.. 
     observable.subscribe(
      null, 
      null, 
      () => this._pubsub.afterRequest.emit("afterRequestEvent"); 
     ); 
     return observable 
    } 


} 

gli emettitori

import {Subject } from 'rxjs/Subject'; 

export class RequestEventEmitter extends Subject<String>{ 
    constructor() { 
     super(); 
    } 
    emit(value) { super.next(value); } 
} 

export class ResponseEventEmitter extends Subject<String>{ 
    constructor() { 
     super(); 
    } 
    emit(value) { super.next(value); } 
} 

Il PubSubService

0.123.
import {Injectable} from 'angular2/core'; 
import {RequestEventEmitter, ResponseEventEmitter} from './emitter'; 

@Injectable() 
export class PubSubService{ 
    beforeRequest:RequestEventEmitter; 
    afterRequest:ResponseEventEmitter; 
    constructor(){ 
     this.beforeRequest = new RequestEventEmitter(); 
     this.afterRequest = new ResponseEventEmitter(); 
    } 
} 

bootstrap App

//main entry point 
import {bootstrap} from 'angular2/platform/browser'; 
import {provide} from 'angular2/core'; 
import {Http, HTTP_PROVIDERS, XHRBackend, RequestOptions} from 'angular2/http'; 
import {HelloWorldComponent} from './hello_world'; 
import {CustomHttp} from './customhttp'; 
import {PubSubService} from './pubsubService' 

bootstrap(HelloWorldComponent, [HTTP_PROVIDERS,PubSubService, 
    provide(Http, { 
     useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, pubsub: PubSubService) 
      => new CustomHttp(backend, defaultOptions, pubsub), 
     deps: [XHRBackend, RequestOptions, PubSubService] 
    }) 
]).catch(err => console.error(err)); 

Ora nel componente di carico la sua facile come la sottoscrizione agli eventi e impostando una proprietà di mostrare o meno

export class LoaderComponent implements OnInit { 
    showLoader = false; 
    _pubsub:PubSubService; 

    constructor(pubsub: PubSubService) { 
    this._pubsub = pubsub; 
    } 
    ngOnInit() { 
    this._pubsub.beforeRequest.subscribe(data => this.showLoader = true); 
    this._pubsub.afterRequest.subscribe(data => this.showLoader = false); 
    } 
} 

Anche se questo finisce per essendo un po 'più codice, se stai cercando di essere informato su ogni richiesta nella tua applicazione questo lo farebbe. Una cosa da notare con l'intercettore è che, poiché viene eseguita una sottoscrizione per ogni richiesta, verranno eseguite immediatamente tutte le richieste, che potrebbero non essere quelle necessarie in casi particolari.Una soluzione a ciò è supportare il normale Http di Angular2 e utilizzare CustomHttp come seconda opzione che potrebbe essere iniettata dove necessario. Penserei che nella maggior parte dei casi l'abbonamento immediato funzionasse bene. Mi piacerebbe sentire esempi di quando non lo farebbe.

+2

Penso che tu possa usare .do invece di iscriverti all'intercetta - [Plunker] (http://plnkr.co/edit/OOxfpPP2HUn4Ujy7UrZx?p=preview). 'intercettazione '(osservabile: Osservabile ): Osservabile { this._pubsub.beforeRequest.emit (" beforeRequestEvent "); return observable.do (() => this._pubsub.afterRequest.emit ("afterRequestEvent")); } ' –

+1

L'uso di' do() 'dovrebbe essere la risposta corretta in quanto @BrianChance ha suggerito che le richieste vengono fatte due volte quando vi iscrivete a loro poiché sono osservabili freddi. Guarda la scheda di rete durante l'esecuzione del plunk. – Yodacheese

+0

L'implementazione descritta da @ d1820, con 'do()' suggerita da @BrianChance ha funzionato come un fascino se utilizzata in Angular v2.0.1, ma presenta un leggero problema dall'aggiornamento alla v2.2.0. Ceteris paribus, i callback ora vengono chiamati due volte (sia onSuccess che onError). Ho cercato un po 'ma non ho trovato cosa c'è che non va; Inoltre non sono riuscito a trovare una CDN 2.2.0 per creare un plunkr, ma dovrebbe essere facile da riprodurre su un progetto locale. Qualcuno ha idea di cosa potrebbe essere sbagliato? – phl

2

Aggiungere una classe DAL (livello di accesso ai dati) comune come questo e utilizzare questa classe DAL nei componenti.

Aggiungi indicatore di caricamento come servizio o componente e usa i tuoi stili personalizzati per questo.

export class DAL { 
    private baseUrl: string = environment.apiBaseUrl; 

    private getConsolidatedPath(path: string) { 
     if (path.charAt(0) === '/') { 
      path = path.substr(1); 
     } 
     return `${this.baseUrl}/${path}`; 
    } 

    private callStack = []; 

    private startCall() { 
     this.loadingIndicator.display(true); 
     this.callStack.push(1); 
    } 

    private endCall() { 
     this.callStack.pop(); 
     if (this.callStack.length === 0) { 
      this.loadingIndicator.display(false); 
     } 
    } 


    public get(path: string) { 
     this.startCall(); 
     return this.http.get(this.getConsolidatedPath(path), { headers: this.getHeaders() }) 
      .map(response => response.json()) 
      .catch(e => this.handleError(e)) 
      .finally(() => this.endCall()); 
    } 
    public put(path: string, data: any) { 
     this.startCall(); 
     return this.http.put(this.getConsolidatedPath(path), data, { headers: this.getHeaders() }) 
      .map(response => response.json()) 
      .catch(e => this.handleError(e)) 
      .finally(() => this.endCall()); 

    } 

    public post(path: string, data: any) { 
     this.startCall(); 
     return this.http.post(this.getConsolidatedPath(path), data, { headers: this.getHeaders() }) 
      .map(response => response.json()) 
      .catch(e => this.handleError(e)) 
      .finally(() => this.endCall()); 
    } 
    public delete(path: string, data: any) { 
     this.startCall(); 
     return this.http.delete(this.getConsolidatedPath(path), { body: data, headers: this.getHeaders() }) 
      .map(response => response.json()) 
      .catch(e => this.handleError(e)) 
      .finally(() => this.endCall()); 
    } 

    constructor(public http: Http, public loadingIndicator: LoadingIndicatorService) { 
    } 

} 
1

Inoltre @tibbus risposta

Questo è meglio per impostare il tipo di "IsLoading" di numero e tenerlo in servizio.

con booleano:

richiesta 1 inizia -> filatore su -> Richiesta 2 inizia -> richiesta 1 finisce -> trottola off -> Richiesta 2 finisce

con il numero:

richiesta 1 inizia -> filatore su -> richiesta 2 inizia -> richiesta 1 finisce -> richiesta 2 estremità -> filatore off

Servizio

@Injectable() 
export class DataService { 
    constructor(private http: Http) { } 

    private set fetchCounter(v:number) { 
     this._fetchCounter = v; 
     this.isLoadingSource.next(this._fetchCounter > 0) 
    } 
    private get fetchCounter() { return this._fetchCounter }; 
    private _fetchCounter:number = 0; 

    private isLoadingSource = new Subject<boolean>(); 
    public isLoading = this.isLoadingSource.asObservable(); 

    public getData() { 
     this.fetchCounter++; 
     return this.http.get('http://jsonplaceholder.typicode.com/posts/2') 
      .map(r => { 
       this.fetchCounter--; 
       return r; 
      }); 
    } 
} 

Hai solo bisogno di iscriverti a isLoading da uno qualsiasi dei tuoi componenti.

Problemi correlati