Angular不会检测到从Ngxs状态发出的Http请求(与区域相关的问题) [英] Http requests made from Ngxs state doesn't get detected by Angular (zone related issue)

查看:136
本文介绍了Angular不会检测到从Ngxs状态发出的Http请求(与区域相关的问题)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

请注意,在http请求期间不需要手动触发进度条(通过服务等).它是自动触发的.

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

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

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 :

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

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

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

  • 发出请求(组件)"
  • 这确实按预期工作,出现进度栏

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

    • 发出请求(要配送的仓库,带区域)"
    • 我问ngx-progressbar的开发人员,他实际上发现了问题并通过将http调用包装在区域中来解决了.确实可以,进度条出现:

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

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

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

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

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

      推荐答案

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

      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.

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

      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

      这是拦截器的代码:

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

      希望这会有所帮助!

      这篇关于Angular不会检测到从Ngxs状态发出的Http请求(与区域相关的问题)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆