每个http请求都使用Angular 2加载器

9
我想做的是:
每当发生HTTP请求时,我希望使用旋转器。换句话说,在我的app.component中发生HTTP请求时,我希望用户看到一个加载屏幕。
我的spinner.component和spinner-service文件与这个问题的答案相同。
我的app.component组件是:
@Component({
    selector: 'todoApi',
    template: `
        <div class="foo">
            <spinner-component></spinner-component>
            <h1>Internship Project</h1>
            <a [routerLink]="['Dashboard']">Dashboard</a>
            <a [routerLink]="['Tasks']">List</a>
            <router-outlet></router-outlet>
        <div>
    `,
    directives: [ROUTER_DIRECTIVES,SpinnerComponent],
    providers: [
        ROUTER_PROVIDERS,
    ]
})

@RouteConfig([
    {
        path: '/dashboard',
        name: 'Dashboard',
        component: DashboardComponent,
        useAsDefault: true
    },{
        path: '/tasks',
        name: 'Tasks',
        component: TaskComponent
    },{
        path: '/detail/:id',
        name: 'TaskDetail',
        component: TaskDetailComponent
    },
])

为了总结一下,每当这些路由中的一个发生http请求时,我都想向用户显示旋转器。我知道这是一个不好的问题,但我是angular 2的新手,如果有人能帮助我,我会非常感激。
非常感谢!编辑!:
Günther的答案解决方案:
我将我的httpspinner-service封装到一个HttpClient组件中,并使用它代替常规的http模块。这是我的HttpClient组件:
import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { SpinnerService} from './spinner-service';

@Injectable()
export class HttpClient {
  constructor(
      private http: Http,
      public spinner: SpinnerService
    ){

  }

  createAuthorizationHeader(headers:Headers) {
    headers.append('Authorization', 'Basic ' + btoa('username:password')); 
  }

  get(url) {
    this.spinner.start();
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.get(url, { headers: headers }).do(data=> {this.spinner.stop()});
  }

  post(url, data) {
    this.spinner.start();
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.post(url, data, { headers: headers }).do(data=> {this.spinner.stop()});
  }
}

也许你可以创建一个自定义服务,通过扩展Angular提供的HttpClass执行HTTP请求,并在每个请求中跟踪你自定义服务的“isBusy”状态。 - Lekhnath
4个回答

7
使用一个服务,该服务在Http(已经有人回答如何将Http包装在您自己的类中)和<spinner-component>之间共享。 在共享服务中维护启动(增加)和完成/失败的HTTP请求的计数器,并每当计数器从0更改为>0或从>0更改为0时通知<spinner-component>以启用或禁用它本身。 请参见https://angular.io/docs/ts/latest/cookbook/component-communication.html

这对我非常有用,Günter!非常感谢!我会尽力尝试一下,看看包装Http是否能解决问题。 - ozata
请问您能否创建一个示例? - gtzinos
1
抱歉,我没有足够的时间。 - Günter Zöchbauer

4

感谢Günter Zöchbauer的回答。这是一个我根据自己需求构建的示例。我没有使用HTTP包装器,这使得使用起来更加简单,但是基于您的计数建议,这个示例适用于多个服务调用。希望它能帮助一些人 :)

  1. Create the Loader service.

    import { Injectable } from '@angular/core';
    import { BehaviorSubject } from 'rxjs/BehaviorSubject';
    
    @Injectable()
    
    export class LoaderService {
        public loaderCounter: BehaviorSubject<number> = new BehaviorSubject<number>(0);
        displayLoader(value: boolean) {
          let counter = value ? this.loaderCounter.value + 1 : this.loaderCounter.value - 1;
          this.loaderCounter.next(counter);
        }
    }
    
  2. Include the service within the providers of your maain module file (Ex: AppModule)

  3. In your main component file (Ex: AppComponent), subscribe to the changes and reflect to the loader (in my case it's a seperate component).

    //Imports
    import { Subscription } from 'rxjs/Subscription';
    import { LoaderService } from './core/loader.service';
    ..
    @Component({
      selector: 'my-app',
      template: `
        <div class="container-fluid content">
          <router-outlet></router-outlet>
        </div>
        <spinner [visible]="displayLoader"></spinner>
      `
    })
    
    export class AppComponent implements OnInit, OnDestroy {
        displayLoader: boolean;
        loaderSubscription: Subscription;
        constructor(private loaderService: LoaderService) {}
    
        ngOnInit() {
            this.loaderSubscription = this.loaderService.loaderCounter.subscribe((counter: number) => {
                this.displayLoader = counter != 0;
            });
        }
    
        ngOnDestroy() {
            this.loaderSubscription.unsubscribe();
        }
    }
    
  4. Using the loader service:

     import { LoaderService } from './core/loader.service';
        ..
        export class SampleComponent implements OnInit {
            constructor(private _service: SomeService, private loader: LoaderService) { }
    
        ngOnInit() {
            this.loader.displayLoader(true);
            this._service.getBalance().subscribe(
                response => ..do something..,
                () => .. error..,
                () => this.loader.displayLoader(false)
            );
        }
    }
    

非常酷......但是为什么你的ajax调用在组件内部?我认为你应该在另一个服务中调用加载器服务方法,其中包含了ajax调用,不是吗? - MadCatm2
根据我对Angular 2的了解,你需要一个服务,然后在组件级别调用和绑定你所拥有的值。我不太确定为什么你需要多个服务,因为你仍然需要将响应与组件内的模型/变量绑定。 - Mark Cutajar
@MarkCutajar 但问题在于我们在组件中故意加载了loader,这缺乏问题的原创性。 - k11k2
@k11k2同意,上面的代码并不适用于每个HttpRequest。为了解决这个问题,需要创建一个HttpWrapper,并从HttpWrapper自身调用loader服务,如MadCatm2所指出的那样。通过发布的代码,可以更好地控制何时显示它,这取决于所需的用例。 - Mark Cutajar

1

仅适用于从现在开始到达此处的人...

使用此解决方案时,如果http请求出现错误,旋转器将不会停止。 确保执行以下操作:

...
return this.http.post(url, data, { headers: headers })
  .do(data=> {this.spinner.stop()},
  err=> {this.spinner.stop());
...

0

你也可以使用Pace.js

添加起来非常容易

<head>
  <script src="/pace/pace.js"></script>
  <link href="/pace/themes/pace-theme-barber-shop.css" rel="stylesheet" />
</head>

您可以在此处找到文档:http://github.hubspot.com/pace/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接