Angular show spinner,用于每个HTTP请求,代码更改次数更少

时间:2018-03-20 13:20:28

标签: angular http

我正在处理现有的Angular应用程序。版本为Angular 4

该应用程序会从许多不同的组件中HTTP调用REST API

我想为每个HTTP请求显示自定义微调器。由于这是一个现有的应用程序,因此很多地方都会调用REST API。在每个地方更改代码都不是一个可行的选择。

我想实现一个解决这个问题的抽象解决方案。

请建议是否有任何选择。

8 个答案:

答案 0 :(得分:16)

@jornare在他的解决方案中有一个好主意。他正在处理多个请求的案件。但是,可以编写更简单的代码,而无需创建新的可观察到的请求并将请求存储在内存中。下面的代码还将RxJS 6与可管道运算符一起使用:

class InitOrderDemo(name: String) {
    val firstProperty = "First property: $name".also(::println)
    fun andAlso (block : (String) -> Int): Int{
        return block(firstProperty)
    }
}

fun main(args : Array<String>) {
    InitOrderDemo("hello").andAlso(String::length).also(::println) 
}

答案 1 :(得分:5)

在Angular 5中出现HttpClient模块。您可以找到 more information there

使用此模块,来一个名为interceptors的东西。

它们允许您为每个HTTP请求执行某些操作。

如果从Http迁移到HttpClient(你应该弃用Http),你可以创建一个可以处理共享服务中变量的拦截器:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  this.sharedService.loading = true;
  return next
    .handle(req)
    .finally(() => this.sharedService.loading = false);
}

现在您只需将此变量用于模板即可。

<spinner *ngIf="sharedService.loading"></spinner>

(务必在显示此微调器的组件中注入您的服务)

答案 2 :(得分:4)

Angular 4+有一个新的HttpClient支持HttpInterceptors。这允许您插入在发出HTTP请求时将运行的代码。

重要的是要注意HttpRequest不是长寿命的Observable,但它们在响应之后终止。此外,如果在响应返回之前取消订阅了observable,则取消请求并且不处理任何处理程序。因此,您可能最终得到一个&#34;悬挂&#34;装载机酒吧,永远不会消失。如果您在应用程序中快速导航,通常会发生这种情况。

为了解决最后一个问题,我们需要创建一个新的Observable来附加拆卸逻辑。

我们返回此而不是原始的Observable。我们还需要跟踪所有请求,因为我们可能一次运行多个请求。

我们还需要一种服务,可以保存和分享我们是否有未决请求的状态。

@Injectable()
export class MyLoaderService {
    // A BehaviourSubject is an Observable with a default value
    public isLoading = new BehaviorSubject<boolean>(false);

    constructor() {}
}

Interceptor使用MyLoaderService

@Injectable()
export class MyLoaderInterceptor implements HttpInterceptor {
    private requests: HttpRequest<any>[] = [];

    constructor(private loaderService: MyLoaderService) { }

    removeRequest(req: HttpRequest<any>) {
        const i = this.requests.indexOf(req);
        this.requests.splice(i, 1);
        this.loaderService.isLoading.next(this.requests.length > 0);
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.requests.push(req);
        this.loaderService.isLoading.next(true);
        return Observable.create(observer => {
          const subscription = next.handle(req)
            .subscribe(
            event => {
              if (event instanceof HttpResponse) {
                this.removeRequest(req);
                observer.next(event);
              }
            },
            err => { this.removeRequest(req); observer.error(err); },
            () => { this.removeRequest(req); observer.complete(); });
          // teardown logic in case of cancelled requests
          return () => {
            this.removeRequest(req);
            subscription.unsubscribe();
          };
        });
    }
}

最后,在我们的Component中,我们可以使用相同的MyLoaderService和async运算符,我们甚至不需要订阅。由于我们要使用的源值来自服务,因此它应该作为Observable共享,以便它获得使用它的渲染范围/区域。如果它只是一个值,它可能无法按需更新您的GUI。

@Component({...})
export class MyComponent {
    constructor(public myLoaderService: MyLoaderService) {}
}

使用async

的示例模板
<div class="myLoadBar" *ngIf="myLoaderService.isLoading | async">Loading!</div>

我假设您知道如何正确提供服务和设置模块。 您还可以在Stackblitz

查看一个有效的示例

答案 3 :(得分:2)

这是一个基本的加载对话框,可以使用angular属性进行切换。只需将*ngIf="loader"添加到中心加载器并相应地设置属性

&#13;
&#13;
.center-loader {
    font-size: large;
    position:absolute;
    z-index:1000;
    top: 50%;
    left: 50%;
    -ms-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
}

@keyframes blink {50% { color: transparent }}
.loader__dot { animation: 1s blink infinite; font-size: x-large;}
.loader__dot:nth-child(2) { animation-delay: 250ms; font-size: x-large;}
.loader__dot:nth-child(3) { animation-delay: 500ms; font-size: x-large;}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<div class="center-loader">
  <strong>Loading
  <span class="loader__dot">.</span>
  <span class="loader__dot">.</span>
  <span class="loader__dot">.</span></strong>
</div>
&#13;
&#13;
&#13;

为每个页面初始化加载程序为true,然后在服务完成后设置为false:

组件顶部

export class MyComponent implements OnInit {
    loader:boolean = true;
//...

<强>的OnInit():

 await this.myService
    .yourServiceCall()
    .then(result => {
        this.resultsSet=result);
        this.loader = false;      // <- hide the loader
      }
    .catch(error => console.log(error));

答案 4 :(得分:0)

您可以使用一些css / gif来显示微调器,并将其用于拦截器类,也可以只使用tur false来显示gif。

   <root-app>
      <div class="app-loading show">
        <div class="spinner"></div>
      </div>
    </root-app>

答案 5 :(得分:0)

取决于您使用 REST服务

的方法

我的方法是

  • # Start SSM automation execution ssm_client.start_automation_execution(DocumentName=your_automation_document_name,Parameters={"InstanceId": [INSTANCE_ID],"VolumeId":[volume_id],"AutomationAssumeRole":[ssm_automation_role_arn]} 并将其放置在应用程序级别的某个位置。
  • Create a component Create a service increment 方法相反。

  • 此服务应按照以下步骤决定是否显示 decrements

      

    为来自客户端的一个请求增加计数器。

         

    减少每个响应loader(component)success上的计数器

答案 6 :(得分:0)

角度拦截器可以在工作时以多种方式使用 在处理和管理HTTP调用进行通信方面表现出色 我们是从客户端Web应用程序制作的。我们可以创建一个 实用程序以使用Interceptor显示Mouse Loader。

请浏览以下帖子,了解LoaderInterceptor的实现:-

Show Loader/Spinner On HTTP Request In Angular using Interceptor

答案 7 :(得分:0)

我正在寻找每个组件都可以使用的东西。 我放了一个计数器,所以微调器在每个请求完成后停止。

这很好用:

export class LoadingStatus{
  public counter: number = 0;
  public isLoading = new Subject<boolean>();

  public reset(){
    this.counter = 0;
    this.isLoading.next(false);
  }
}

export function triggerLoading<T>(status: LoadingStatus): (source: Observable<T>) => Observable<T> {
  return (source: Observable<T>): Observable<T> => source.pipe(
    prepare(() => {
      if(status != null){
        status.counter++;
        status.isLoading.next(true)
      }
    }    ),
    finalize(() => {
      if(status != null){
        status.counter--;
        // if there is something like a flikering, maybe use a delay.
        if(status.counter <= 0) {
          status.counter = 0;
          status.isLoading.next(false)
        }
      }
    })
  )
}

然后像这样调用它:

public loadingStatus $ =新的LoadingStatus();

public makeRequest(){
   this.myService.load()
    .pipe(triggerLoading(this.loadingStatus$))
    .subscribe(v => {});
}

HTML:

<div class="loading-spinner" *ngIf="loadingStatus$?.isLoading | async"></div>