Angular不会检测到从Ngxs状态发出的Http请求(与区域相关的问题)

时间:2018-07-08 19:20:05

标签: angular ngxs

我正在使用ngx-progressbar,它与从服务,组件或解析器中启动的http请求一起正常工作。

请注意,在http请求期间无需手动触发进度条(通过服务等)。它会自动触发。

不幸的是,当从NGXS状态内发出http请求时,它无法按预期工作:

stackblitz

我为每种情况创建了一个按钮:

  • “发出请求(要配送的商品,没有区域)”

这不起作用,没有进度条出现(或出现,但挂起并且未完成到100%)

import java.util.Scanner;
public class Practice {
    public static void main(String[] args) {
            Scanner in = new Scanner(System.in);

            int number1 = (int)(System.currentTimeMillis() % 10);
            int number2 = (int)(System.currentTimeMillis() / 10 % 10);
            int answer = in.nextInt();



            System.out.print("What is " +  number1 + " + " + number2 + "?");


            System.out.println(number1 + " + " + number2 + " = " + answer + " is " + (number1 + number2 == answer));


        }

    }
  • “发出请求(组件)”

这确实按预期工作,出现进度栏

@Action(LoadUrl)
    load({ setState }: StateContext<string>) {
        return this.http.get<any>('https://jsonplaceholder.typicode.com/posts/1').pipe(tap(res => console.log(res)));
    }
  • “发出请求(要配送的商品,带区域)”

我问ngx-progressbar的开发人员,他实际上发现了问题并通过将http调用包装在区域中来解决了。确实可以,进度条出现:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
// ...
  makeRequestComponent() {
    this.http.get<any>('https://jsonplaceholder.typicode.com/posts/1').pipe(tap(res => console.log(res))).subscribe();
  }
}

由于我希望进度条随每个http请求一起出现,因此解决此问题的正确方法是什么?

有没有办法告诉NGXS在区域内运行每个http请求?

1 个答案:

答案 0 :(得分:4)

由于性能原因,当前ngxs会在角度区域外运行所有操作。 如果您订阅动作流或store.select,则默认情况下将在角度区域执行您的订阅。 State对象中的动作处理程序在角度区域之外运行。这是ngxs的默认行为。我认为关闭此“在区域外运行”功能是一项有效的要求。请您记录一下要求此功能的github问题。

回到您的迫在眉睫的问题,我制作了一个Http拦截器,您可以添加它以强制http调用返回到角度区域(您只需要此即可触发更改检测)。我分叉了您的stackblitz并将其添加到此处: https://stackblitz.com/edit/ngx-progressbar-inzone?file=src%2Fapp%2FNgZoneHttpInterceptor.ts

这是拦截器的代码:

import { Injectable, Optional, Inject, NgZone, NgModule, ModuleWithProviders } from '@angular/core';
import { HTTP_INTERCEPTORS, HttpInterceptor, HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable, Observer } from 'rxjs';

@NgModule({
})
export class NgZoneHttpInterceptorModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: NgZoneHttpInterceptorModule,
      providers: [
        { provide: HTTP_INTERCEPTORS, useClass: NgZoneHttpInterceptor, multi: true }
      ]
    };
  }
}

@Injectable()
export class NgZoneHttpInterceptor implements HttpInterceptor {

  constructor(private _ngZone: NgZone) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {    
    return this._ngZone.run(() => {
      return next.handle(req).pipe(enterZone(this._ngZone));
    });
  }
}


function enterZone<T>(zone: NgZone) {
  return (source: Observable<T>) => {
    return new Observable((sink: Observer<T>) => {
      return source.subscribe({
        next(x) { zone.run(() => sink.next(x)); },
        error(e) { zone.run(() => sink.error(e)); },
        complete() { zone.run(() => sink.complete()); }
      });
    });
  };
}

希望这会有所帮助!