Angular2:显示每个HTTP请求的活动指示器并隐藏视图直到完成

时间:2016-05-27 18:34:13

标签: http angular

我是Angular2的新手,我想知道是否有办法为每个HTTP请求显示一个活动指示器并隐藏视图直到完成?

4 个答案:

答案 0 :(得分:17)

一种方法是为Angular2 Http编写一个拦截器。通过创建自己的http实例,您可以在使用"提供"引导应用程序时交换它。方法。完成此操作后,可以创建一个PubSub服务,以便从Http拦截器发布和订阅这些事件,并在每个请求发出事件之前和之后发出。

可以在Plunker

上看到一个实例

拦截器:

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
    }


}

发射者

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); }
}

PubSubService

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();
   }
}

引导应用

//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));

现在在你的加载组件中,它就像订阅事件和设置要显示的属性一样简单

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);   
  }
}

虽然最终会有更多代码,但如果您希望在应用程序中的每个请求上收到通知,那么就可以执行此操作。拦截器需要注意的一件事是,因为正在为每个请求立即执行所有请求的订阅,这可能不是您在特定情况下所需要的。解决方案是支持常规的Angular2 Http并使用CustomHttp作为第二个选项,可以在需要时注入。我认为在大多数情况下,立即订阅可以正常工作。我很想听听它不会的例子。

答案 1 :(得分:16)

是的,您需要为每个视图处理:

  • 您可以为http请求提供服务,该服务将返回 可观察
  • 在组件中,您将具有加载状态
  • 您需要在从中请求数据之前将加载状态设置为true 服务器,然后在完成数据提取时将其设置为false。
  • 在模板中使用 ngIf 隐藏/显示加载或内容

    例如:

服务:

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

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

组件:

@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);
      } 
  } 
}

可在此处找到一个工作示例:http://plnkr.co/edit/HDEDDLOeiHEDd7VQaev5?p=preview

答案 2 :(得分:3)

另外@tibbus回复

最好设置“isLoading”类型的数字并保持服务。

with boolean:

请求1开始 - &gt;旋转器 - &gt;请求2开始 - &gt;请求1结束 - &gt;旋转器 - &gt;请求2结束

号码:

请求1开始 - &gt;旋转器 - &gt;请求2开始 - &gt;请求1结束 - &gt;请求2结束 - &gt;旋转器

<强>服务

@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;
            });
    }
} 

您只需从任何组件订阅isLoading。

答案 3 :(得分:2)

像这样添加一个通用的DAL(数据访问层)类,并在组件中使用这个DAL类。

将加载指示符添加为服务或组件,并使用自定义样式。

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) {
    }

}