Http requests made from Ngxs state doesn't get detected by Angular (zone related issue)

孤街浪徒 提交于 2019-12-18 09:51:41

问题


I'm using ngx-progressbar and it works fine with http request started from within services, components or resolvers.

Note that no manual triggering of the progress bar (via service, etc) during http request is needed. It is triggered automatically.

Unfortunately it doesn't work as expected when making an http request from within an NGXS State :

stackblitz :

I created for every case a button :

  • "Make Request (Dispatch to store, w/o zone)"

This does not work, no progress bar appears (or it appears but hangs and does not complete to 100%)

@Action(LoadUrl)
    load({ setState }: StateContext<string>) {
        return this.http.get<any>('https://jsonplaceholder.typicode.com/posts/1').pipe(tap(res => console.log(res)));
    }
  • "Make Request (Component)"

This does work as expected, progress bar appears

@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();
  }
}
  • "Make Request (Dispatch to store, w/ zone)"

I asked the developer of ngx-progressbar and he actually found the issue and solved it by wrapping the http call inside zone. This does work, progress bar appears :

@State<string>({
  name: 'testState',
  defaults: ''
})
export class TestUrlState {

  constructor(private http: HttpClient, private zone: NgZone) { }

  @Action(LoadUrl)
  load({ setState }: StateContext<string>) {

    this.zone.run(() => {

      this.http.get<any>('https://reqres.in/api/users?delay=2').pipe(
        tap(res => console.log(res))
      ).subscribe();

    });
  }
}

Since I want the progress bar to appear with every http request, what would be the right approach to solve this ?

Is there a way to tell NGXS to run every http request inside zone ?


回答1:


Currently ngxs runs all your actions outside the angular zone for performance reasons. If you subscribe to the action stream or a store.select then your subscription will be executed in the angular zone by default. The action handlers within the State object are run outside the angular zone. This is the default behaviour for ngxs. I think that it is a valid requirement to turn off this "run outside of zone" feature. Please could you log a github issue requesting this feature.

Back to your immediate problem, I made a Http Interceptor that you can add to force the http calls back into the angular zone (you only really need this to trigger the change detection). I forked your stackblitz and added it here: https://stackblitz.com/edit/ngx-progressbar-inzone?file=src%2Fapp%2FNgZoneHttpInterceptor.ts

Here is the code for the interceptor:

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

Hope this helps!



来源:https://stackoverflow.com/questions/51235284/http-requests-made-from-ngxs-state-doesnt-get-detected-by-angular-zone-related

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!