Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何在Angular中实现全局加载程序_Javascript_Angular_Rxjs_Angular Cdk - Fatal编程技术网

Javascript 如何在Angular中实现全局加载程序

Javascript 如何在Angular中实现全局加载程序,javascript,angular,rxjs,angular-cdk,Javascript,Angular,Rxjs,Angular Cdk,我有一个全局加载器,其实现方式如下: "@angular/cdk": "~7.0.0", "@angular/material": "~7.0.0", 核心模块: router.events.pipe( filter(x => x instanceof NavigationStart) ).subscribe(() => loaderService.show()); router.events.pipe( filter(x => x instanceof Navig

我有一个全局加载器,其实现方式如下:

"@angular/cdk": "~7.0.0",
"@angular/material": "~7.0.0",
核心模块:

router.events.pipe(
  filter(x => x instanceof NavigationStart)
).subscribe(() => loaderService.show());

router.events.pipe(
  filter(x => x instanceof NavigationEnd || x instanceof NavigationCancel || x instanceof NavigationError)
).subscribe(() => loaderService.hide());
装载机服务:

@Injectable({
    providedIn: 'root'
})
export class LoaderService {

    overlayRef: OverlayRef;
    componentFactory: ComponentFactory<LoaderComponent>;
    componentPortal: ComponentPortal<LoaderComponent>;
    componentRef: ComponentRef<LoaderComponent>;

    constructor(
        private overlay: Overlay,
        private componentFactoryResolver: ComponentFactoryResolver
    ) {
        this.overlayRef = this.overlay.create(
            {
                hasBackdrop: true,
                positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically()
            }
        );

        this.componentFactory = this.componentFactoryResolver.resolveComponentFactory(LoaderComponent);

        this.componentPortal = new ComponentPortal(this.componentFactory.componentType);
    }

    show(message?: string) {
        this.componentRef = this.overlayRef.attach<LoaderComponent>(this.componentPortal);
        this.componentRef.instance.message = message;
    }

    hide() {
        this.overlayRef.detach();
    }
}
与此相反:

"@angular/cdk": "~7.2.0",
"@angular/material": "~7.2.0",
我已经确定了7.1.0版中发布的错误提交,并将我的问题发布在相关的网站上。它修复了
覆盖
的淡出动画

什么是v7.1+兼容的方式来获得所需的行为? 我认为,最好的办法是:仅在必要时显示加载程序,但
NavigationStart
不包含所需信息。
我希望避免以一些去抖动行为结束。

在意识到延迟是UX方面的一个很好的解决方案后,这里是我的结束,因为它允许加载程序仅在加载时间值得显示加载程序时才显示

我不喜欢这个解决方案,因为它意味着在两个可观测对象之间共享一个状态,而可观测对象是纯管道,而不是副作用和共享状态

计数器=0;
路由器.事件.管道(
过滤器(x=>x个NavigationStart实例),
延迟(200),
).订阅(()=>{
/*
如果此条件为true,则对应于此NavigationStart结束的事件
尚未通过,因此我们显示加载程序
*/
if(this.counter==0){
loaderService.show();
}
这个.counter++;
});
路由器.事件.管道(
过滤器(x=>x导航实例结束| | x导航实例取消| | x导航实例错误)
).订阅(()=>{
这个柜台;
loaderService.hide();
});

我们在系统中实现加载器的方式以及例外列表:

export class LoaderInterceptor implements HttpInterceptor {
  requestCount = 0;

  constructor(private loaderService: LoaderService) {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    if (!(REQUEST_LOADER_EXCEPTIONS.find(r => request.url.includes(r)))) {
      this.loaderService.setLoading(true);
      this.requestCount++;
    }

    return next.handle(request).pipe(
      tap(res => {
        if (res instanceof HttpResponse) {
          if (!(REQUEST_LOADER_EXCEPTIONS.find(r => request.url.includes(r)))) {
            this.requestCount--;
          }
          if (this.requestCount <= 0) {
            this.loaderService.setLoading(false);
          }
        }
      }),
      catchError(err => {
        this.loaderService.setLoading(false);
        this.requestCount = 0;
        throw err;
      })
    );
  }
}

试图改进@Guerric p的概念,我想如果你定义了一个可观察的,比如:

  loading$ = this.router.events.pipe(
    filter(
      x =>
        x instanceof NavigationStart ||
        x instanceof NavigationEnd ||
        x instanceof NavigationCancel ||
        x instanceof NavigationError
    ),
    map(x => (x instanceof NavigationStart ? true : false)),
    debounceTime(200),
    tap(x => console.log(x))
  );
你有

<div *ngIf="loading$|async">Loadding....</div>
所以,我们可以做,例如

this.dataService.loading.next(true)
this.dataService.getData(time).subscribe(res=>{
  this.dataService.loading.next(false)
  this.response=res;
})
注意:您可以检查stackblitz,其中一个组件不会延迟,因此“加载”不会显示,而两个组件的延迟是因为一个CanActivate Guard和因为调用ngOnInit中的服务


注2:在这个示例中,我们手动调用“this.dataService.load.next(true | false)”。我们可以通过创建一个操作符来改进它

我的评论的最后一次更新总是等待200毫秒。当然,我们不希望在导航结束时或在可观测的时间结束时等待。因此,我们可以通过一些类似于
debounce(x=>x?timer(200):EMPTY)
的方法重新使用
debounceTime(200)
,但这使得我们在ngOnInit中有一个延迟搜索的组件,“loader”flick

因此,我决定在路由器中使用“数据”来指示哪些组件具有ngOnInit

想象一下

const routes: Routes = [
  { path: 'one-component', component: OneComponent },
  { path: 'two-component', component: TwoComponent,
               canActivate:[FoolGuard],data:{initLoader:true}},
    { path: 'three-component', component: ThreeComponent,
               canActivate:[FoolGuard],data:{initLoader:true} },
  ];
我创建了一个服务,当路由器在数据“initLoader”中包含

在通话开始时向主题发送true,在通话结束时向主题发送false

好吧,我可以做这样的服务

/*Service*/
@Injectable({
  providedIn: "root"
})
export class DataService {
  loading: Subject<boolean> = new Subject<boolean>();
  constructor(private router: Router){}

  //I use the Nils' operator
  getData(time: number) {
    return of(new Date()).pipe(delay(time),indicate(this.loading));
  }

  getLoading(): Observable<any> {
    let wait=true;
    return merge(
      this.loading.pipe(map(x=>{
        wait=x
        return x
        })),
      this.router.events
        .pipe(
          filter(
            x =>
              x instanceof NavigationStart ||
              x instanceof ActivationEnd ||
              x instanceof NavigationCancel ||
              x instanceof NavigationError || 
              x instanceof NavigationEnd
          ),
          map(x => {
            if (x instanceof ActivationEnd) {
              wait=x.snapshot.data.wait|| false;
              return true;
            }
            return x instanceof NavigationStart ? true : false;
          })
        ))
        .pipe(
          debounce(x=>wait || x?timer(200):EMPTY),
        )
  }

是否有可能在没有触发器的情况下执行
loaderService.hide()
?您是否询问它是否从其他地方调用?这可能是我从未考虑过的选项,但我的意思是,它可以在没有任何触发器的情况下执行,并且您使用的符号只是解释为要执行的代码,而不是带有函数的OOP结构。@David抱歉,我真的不明白您的意思。我已经在Angular CDK中识别了错误的pull请求:它只适用于http请求,不适用于懒惰模块我认为最好创建一个独特的可观察对象。在html管道中使用异步是没有必要的。我们的示例应该显示一个使用Angular的覆盖的加载程序,因为最初的问题是覆盖在不需要时短暂显示。Guerric,我用一个带*ngIf的简单div,您可以使用任何组件或带覆盖的div。老实说,我不喜欢使用“计数器”或订阅两个观察值,但这是个人观点。使用一个独特的可观测值和一个去盎司,避免它,但当然,你的代码也是一个很好的方法。我也不喜欢它,这就是为什么我要求另一个答案与我的赏金。带有*ngIf的div的行为与覆盖不同,因为覆盖从v7开始逐渐消失,在以前的版本中,我们总是可以在很短的时间间隔内显示/隐藏它,它不可见,但这是因为v7Your
debounceTime
应用于
true
false
事件,这可能会导致跳过事件needed@GuerricP,我更新了stackblitz。我在“第二个组件”中使用AuthGuard。您可以在更改第二个组件之前看到“加载”。我认为在延迟加载的情况下应该可以工作。我希望这个例子能帮助我更好地解释。
this.dataService.loading.next(true)
this.dataService.getData(time).subscribe(res=>{
  this.dataService.loading.next(false)
  this.response=res;
})
const routes: Routes = [
  { path: 'one-component', component: OneComponent },
  { path: 'two-component', component: TwoComponent,
               canActivate:[FoolGuard],data:{initLoader:true}},
    { path: 'three-component', component: ThreeComponent,
               canActivate:[FoolGuard],data:{initLoader:true} },
  ];
/*Nils' operator: https://nils-mehlhorn.de/posts/indicating-loading-the-right-way-in-angular
*/
export function prepare<T>(callback: () => void): (source: Observable<T>) => Observable<T> {
  return (source: Observable<T>): Observable<T> => defer(() => {
    callback();
    return source;
  });
}
export function indicate<T>(indicator: Subject<boolean>): (source: Observable<T>) => Observable<T> {
  return (source: Observable<T>): Observable<T> => source.pipe(
    prepare(() => indicator.next(true)),
    finalize(() => indicator.next(false))
  )
}
  myObservable.pipe(indicate(mySubject)).subscribe(res=>..)
/*Service*/
@Injectable({
  providedIn: "root"
})
export class DataService {
  loading: Subject<boolean> = new Subject<boolean>();
  constructor(private router: Router){}

  //I use the Nils' operator
  getData(time: number) {
    return of(new Date()).pipe(delay(time),indicate(this.loading));
  }

  getLoading(): Observable<any> {
    let wait=true;
    return merge(
      this.loading.pipe(map(x=>{
        wait=x
        return x
        })),
      this.router.events
        .pipe(
          filter(
            x =>
              x instanceof NavigationStart ||
              x instanceof ActivationEnd ||
              x instanceof NavigationCancel ||
              x instanceof NavigationError || 
              x instanceof NavigationEnd
          ),
          map(x => {
            if (x instanceof ActivationEnd) {
              wait=x.snapshot.data.wait|| false;
              return true;
            }
            return x instanceof NavigationStart ? true : false;
          })
        ))
        .pipe(
          debounce(x=>wait || x?timer(200):EMPTY),
        )
  }
this.anotherService.getData().pipe(
   indicate(this.dataService.loading)
).subscribe(res=>{....})