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

Angular2:显示每个HTTP请求的活动指示器,并隐藏视图,直到完成,http,angular,Http,Angular,我是Angular2的新手,我想知道是否有任何方法可以为每个HTTP请求显示活动指示器,并在完成之前隐藏视图?是的,您需要为每个视图处理该问题: 您可以为http请求提供一个服务,该服务将返回 可观察 在组件中,您将处于加载状态 在从服务器请求数据之前,需要将加载状态设置为true 服务器,然后在完成数据获取时将其设置为false 在模板中,使用ngIf隐藏/显示加载或内容 例: 服务: @Injectable() export class DataService { constru

我是Angular2的新手,我想知道是否有任何方法可以为每个HTTP请求显示活动指示器,并在完成之前隐藏视图?

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

  • 您可以为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);
      } 
  } 
}
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
    }


}
@组件({
选择器:“我的应用程序”,
模板:`
加载。。
内容…大量内容
更多内容` }) 导出类应用程序{ 加载:布尔; 构造函数(私有数据服务:数据服务){} 恩戈尼尼特(){ //开始从服务器加载数据 这是。加载=真; this.dataService.getData().delay(1500).subscribe( 请求数据=>{ //数据加载完成 这一点:加载=错误; log('AppComponent',requestData); } } }

这里可以找到一个工作示例:

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

一个活生生的例子可以在网上看到

拦截器:

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


}
启动应用程序

//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作为第二个选项,可以在需要时注入。我认为在大多数情况下,立即订阅可以正常工作。我很想听到这样的例子。

添加一个类似这样的通用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) {
    }

}
此外,回应

最好设置号码的“isLoading”类型,并将其保持在服务状态

使用布尔值:

请求1开始->旋转器打开->请求2开始->请求1结束->旋转器关闭->请求2结束

编号:

请求1开始->旋转器打开->请求2开始->请求1结束->请求2结束->旋转器关闭

服务

@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;
            });
    }
} 
@Injectable()
导出类数据服务{
构造函数(私有http:http){}
专用集获取计数器(v:编号){
这个。_fetchCounter=v;
this.isLoadingSource.next(this.\u fetchCounter>0)
}
private get fetchCounter(){返回此值。_fetchCounter};
私有_fetchCounter:number=0;
private isLoadingSource=新主题();
public isLoading=this.isLoadingSource.asObservable();
公共获取数据(){
这个.fetchCounter++;
返回此.http.get('http://jsonplaceholder.typicode.com/posts/2')
.map(r=>{
这是我的柜台;
返回r;
});
}
} 

你只需要订阅任何组件的isLoading。

我认为你可以使用.do而不是订阅intercept-.
intercept(observable:observable):observable{this.\u pubsub.beforeRequest.emit(“beforeRequestEvent”);return observable.do(()=>this.\u pubsub.afterRequest.emit(“afterRequestEvent”))}
使用
do()
应该是正确的答案,因为@BrianChance建议在订阅请求时会发出两次请求,因为它们是冷可观察的。运行plunk时请查看网络选项卡。@d1820描述的实现,使用
do()
由@BrianChance建议,在Angular v2.0.1中使用时效果很好,但升级到v2.2.0后出现了一个小问题。在其他条件相同的情况下,回调现在被调用两次(onSuccess和onError)。我搜索了一点,但没有找到问题所在;我也没有找到制作plunkr的2.2.0 CDN,但它应该很容易在本地项目上复制。有人知道有什么问题吗?当我注释CustomHttp.request方法时,它只运行一次。猜测super.get now调用CustomHttp.request,调用super.Mean您可能只需要实现请求而不是每个动词所有人现在都向基本请求方法发出请求,并覆盖相应的选项。要解决此问题,只需从CustomHttp模块中注释掉或删除Post、Put、Delete和Get方法。[Plunker]()。截取只需要在基本请求原型方法中完成。简单易行。很好。ty