Angular 如何在等待从服务中获取数据时显示加载微调器

Angular 如何在等待从服务中获取数据时显示加载微调器,angular,Angular,我已经建立了一个可观察的服务来帮助我持久化数据,但是我需要一种方法来显示加载微调器,同时数据等待来自可观察的服务 我的服务: imports [...] import { Observable, BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class OrganisationsService { private organisations$ = new BehaviorSubjec

我已经建立了一个可观察的服务来帮助我持久化数据,但是我需要一种方法来显示加载微调器,同时数据等待来自可观察的服务

我的服务:

imports [...]

import { Observable, BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class OrganisationsService {
  private organisations$ = new BehaviorSubject<Organisation[]>([
    {
      [...]
    }
  ]);

  constructor(private http: HttpClient) {
    this.http
      .get(
        environment.apicall
      )
      .subscribe((data) => this.organisations$.next(data['organisations']));
  }

  getList(): Observable<Organisation[]> {
    return this.organisations$.asObservable();
  }
以下是我的组件:

    imports ...

    import UIkit from 'uikit';
    import { Observable } from 'rxjs';

    @Component({
      selector: 'app-organisationlist',
      templateUrl: './organisationlist.component.html',
      styleUrls: ['./organisationlist.component.css']
    })
    export class OrganisationlistComponent implements OnInit {

      public loading = true;

      public organisations$: Observable<Organisation[]>;

      constructor(public organisationsService: OrganisationsService) {}

      ngOnInit() {
        this.getData().then(() => {
          this.loading = false;
        });
      }

      async getData() {
        this.organisations$ = this.organisationsService.getList();
      }
}
以下是我使用Uikit的布局:

  <div *ngIf="loading" uk-spinner></div>
  <div>
    <ul class="uk-list">
      <li *ngFor="let organisation of organisations$ | async">
        {{ organisation.name }}
      </li>
    </ul>
  </div>
显然,getData函数虽然声明为异步,但实际上不是异步的,并且会立即返回。因此,这个.loading在init上是'false',因此它不会显示

有很多答案表明,当订阅发生在组件中时,这可能是如何工作的,但由于这是服务中的共享数据,我无法找到在将this.loading设置为false之前等待数据的方法

非常感谢。

您可以在OrganizationListComponent中进行更多更改

您可以在OrganizationListComponent中进行更多更改

更改下面的行

async getData() {
    this.organisations$ = this.organisationsService.getList();
  }

这应该是可行的,因为这是您正在等待完成的可观察对象,而不是getData方法更改下一行

async getData() {
    this.organisations$ = this.organisationsService.getList();
  }


这应该是可行的,因为这是您正在等待完成的可观察到的,而不是getData方法

谢谢大家的回答,但是这些建议都没有达到问题中所述的目的,但我确实制定了一个解决方案,最终得到了一个可行的解决方案。我们开始:

在该服务中,我创建了一个名为loading$的新可观察对象,并将其设置为一个新的BehaviorSubjecttrue

然后我创建了一个getter:

getLoading:可观察{ 返回此。正在加载$; }

然后,我在订阅函数的第三个参数中将HTTP调用包装为true,然后将loading设置为false

因此,该服务如下所示:

import { Observable, BehaviorSubject, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class OrganisationsService {
  private organisations$ = new BehaviorSubject<Organisation[]>([
    {
      [...]
    }
  ]);

  //initiate the new Loading variable via BehaviorSubject and set it to "true" from the beginning.
  public loading$ = new BehaviorSubject<boolean>(true);

  constructor(private http: HttpClient) {

    //set the loading$ to true again just before we start the HTTP call
    this.loading$.next(true);


    this.http
      .get(environment.apicall)
      .subscribe(

        //onNext method, where we get the data
        (data) => this.organisations$.next(data['organisations']),

        //onError method (Blank for now)
        () => {},

        //onComplated method, now that we have the data we can set the loading to false
        () => {
          this.loading$.next(false);
        }
      );
  }

  getLoading(): Observable<boolean> {
    return this.loading$;
  }

  getList(): Observable<Organisation[]> {
    return this.organisations$;
  }
在我看来:

  <div>
    <div uk-spinner *ngIf="loading$ | async"></div>
    <ul class="uk-list">
      <li *ngFor="let organisation of organisations$ | async">
        {{ organisation.name }}
      </li>
    </ul>
  </div>

谢谢大家的回答,但这些建议都没有达到问题中所述的目的,但我确实制定了一个解决方案,最终得到了一个可行的解决方案。我们开始:

在该服务中,我创建了一个名为loading$的新可观察对象,并将其设置为一个新的BehaviorSubjecttrue

然后我创建了一个getter:

getLoading:可观察{ 返回此。正在加载$; }

然后,我在订阅函数的第三个参数中将HTTP调用包装为true,然后将loading设置为false

因此,该服务如下所示:

import { Observable, BehaviorSubject, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class OrganisationsService {
  private organisations$ = new BehaviorSubject<Organisation[]>([
    {
      [...]
    }
  ]);

  //initiate the new Loading variable via BehaviorSubject and set it to "true" from the beginning.
  public loading$ = new BehaviorSubject<boolean>(true);

  constructor(private http: HttpClient) {

    //set the loading$ to true again just before we start the HTTP call
    this.loading$.next(true);


    this.http
      .get(environment.apicall)
      .subscribe(

        //onNext method, where we get the data
        (data) => this.organisations$.next(data['organisations']),

        //onError method (Blank for now)
        () => {},

        //onComplated method, now that we have the data we can set the loading to false
        () => {
          this.loading$.next(false);
        }
      );
  }

  getLoading(): Observable<boolean> {
    return this.loading$;
  }

  getList(): Observable<Organisation[]> {
    return this.organisations$;
  }
在我看来:

  <div>
    <div uk-spinner *ngIf="loading$ | async"></div>
    <ul class="uk-list">
      <li *ngFor="let organisation of organisations$ | async">
        {{ organisation.name }}
      </li>
    </ul>
  </div>

补充我的评论使用。我喜欢这个解决方案,因为它是透明的,看到一个调用是简单的,例如

return this.httpClient.get('.....').pipe(
      indicate(this.loading$))
此外,您可以使用任何可观察的,而不仅仅是调用http。使用rxjs运算符进行,例如,使用forkJoin进行多个http调用,或者使用switchMap进行两个调用您只有一个管道,不要使调用过载,也不要导致flick

接线员是

export const prepare = <T>(callback: () => void) => {
    return (source: Observable<T>): Observable<T> =>
        defer(() => {
            callback();
            return source;
        });
};

export const indicate = <T>(indicator: Subject<boolean>) => {
    let alive = true;
    return (source: Observable<T>): Observable<T> =>
        source.pipe(
            prepare(() =>
                timer(500)
                    .pipe(
                        takeWhile(() => alive),
                        take(1)
                    )
                    .subscribe(() => {
                        indicator.next(true);
                    })
            ),
            finalize(() => {
                alive = false;
                indicator.next(false);
            })
        );
};

export const toClass = <T>(ClassType: { new(): T }) => (
    source: Observable<T>
) => source.pipe(map(val => Object.assign(new ClassType(), val)));
更新 我将$loading设置为属于服务,但您可以将$loading设置为属于组件,并在组件中使用管道

  loading$ = new Subject<boolean>()
  loadData2()
  {
    this.data="";
    this.dataService.getData2().pipe(
      indicate(this.loading$))
      .subscribe(res=>{
      this.data=res
    })
  }

<div *ngIf="(loading$ | async)">
    loading...
</div>

您可以在

中看到使用的补充我的评论。我喜欢这个解决方案,因为它是透明的,看到一个调用是简单的,例如

return this.httpClient.get('.....').pipe(
      indicate(this.loading$))
此外,您可以使用任何可观察的,而不仅仅是调用http。使用rxjs运算符进行,例如,使用forkJoin进行多个http调用,或者使用switchMap进行两个调用您只有一个管道,不要使调用过载,也不要导致flick

接线员是

export const prepare = <T>(callback: () => void) => {
    return (source: Observable<T>): Observable<T> =>
        defer(() => {
            callback();
            return source;
        });
};

export const indicate = <T>(indicator: Subject<boolean>) => {
    let alive = true;
    return (source: Observable<T>): Observable<T> =>
        source.pipe(
            prepare(() =>
                timer(500)
                    .pipe(
                        takeWhile(() => alive),
                        take(1)
                    )
                    .subscribe(() => {
                        indicator.next(true);
                    })
            ),
            finalize(() => {
                alive = false;
                indicator.next(false);
            })
        );
};

export const toClass = <T>(ClassType: { new(): T }) => (
    source: Observable<T>
) => source.pipe(map(val => Object.assign(new ClassType(), val)));
更新 我将$loading设置为属于服务,但您可以将$loading设置为属于组件,并在组件中使用管道

  loading$ = new Subject<boolean>()
  loadData2()
  {
    this.data="";
    this.dataService.getData2().pipe(
      indicate(this.loading$))
      .subscribe(res=>{
      this.data=res
    })
  }

<div *ngIf="(loading$ | async)">
    loading...
</div>

您可以在

中看到,您可以创建自己的服务,其中包括行为主体和公共加载功能。app.service.ts

public loading: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false)
public isLoading(state: boolean): void { this.loading.next(state) }
在app.component.html视图中,导入共享微调器,如下所示:

<spinner [showSpinner]="loading$ | async"></spinner>
共享组件@input showSpinner带有*ngIf标志以决定 是否显示微调器

最后,在返回的api中,调用可以编写如下内容:

this.appService.isLoading(true)
return this.callApiByMethod(options).pipe(
  finalize(() => this.appService.isLoading(false)),
  shareReplay(),
) as Observable<T>

我在这里使用rx finalize,因此它将创建一个回调函数,可以处理成功和错误响应。

您可以创建自己的服务,其中包括行为主体和公共加载函数。app.service.ts

public loading: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false)
public isLoading(state: boolean): void { this.loading.next(state) }
在app.component.html视图中,导入共享微调器,如下所示:

<spinner [showSpinner]="loading$ | async"></spinner>
共享组件@input showSpinner带有*ngIf标志以决定 是否显示微调器

最后,在返回的api中,调用可以编写如下内容:

this.appService.isLoading(true)
return this.callApiByMethod(options).pipe(
  finalize(() => this.appService.isLoading(false)),
  shareReplay(),
) as Observable<T>

我在这里使用rx finalize,因此它将创建一个回调函数,可以处理成功和错误响应。

最好的:感谢Eliseo。。。由于某些原因,带有ng模板的指示器对我不起作用。我用一个答案和一个例子来补充评论,我希望这能帮助你回答你的问题吗?最好的:谢谢Eliseo。。。由于某些原因,带有ng模板的指示器对我不起作用。我用一个答案和一个例子来补充评论,我希望这能帮助你回答你的问题吗?谢谢你,维杰。但这也不行。。。因为getData会立即返回,所以then块会被插入
立即返回,因此此.loading立即为假。谢谢Vijay。但这也不行。。。因为getData会立即返回,所以then块会立即返回,因此this.loading立即为false。谢谢Vilsad,但这也不起作用。。。同样,getData会立即返回,因此此.loading立即为false。谢谢Vilsad,但这也不起作用。。。同样,getData会立即返回,因此this.loading立即为false。