【问题标题】:Loading spinner is not showing while doing a HTTP request in Angular在 Angular 中执行 HTTP 请求时未显示加载微调器
【发布时间】:2020-04-27 21:56:03
【问题描述】:

我正在使用 Angular 8 做一个 Web 应用程序,我想在执行 HTTP 请求时显示一个加载微调器。

我的实现没有显示加载微调器,我找不到原因。

服务

@Injectable()
export class UserService {

  // Caching
  private bsResource: BehaviorSubject<string[]> = new BehaviorSubject([]);
  private readonly resources$: Observable<string[]> = this.bsResource.asObservable();

  constructor(
    private http: HttpClient
  ) {
  }

  // Calling this method the loading spinner is not showing.
  getResources(): Observable<string[]> {
    if (this.bsResource.getValue().length === 0) {
      this.fetchResourceList().toPromise().then(res => this.bsResource.next(res));
    }

    return this.resources$;
  }

  // The loading spinner appears if I use this method directly and make this method public.
  private fetchResourceList(): Observable<string[]> {
    return this.http.get<string[]>('MY_URL');
  }
}

组件:

@Component({
  templateUrl: './create-new.component.html',
  styleUrls: ['./create-new.component.scss']
})
export class CreateNewComponent {
  resourceList$: Observable<string[]> = this.service.getResources();

  constructor(
    private service: UserService
  ) { }
}

模板

<div class="form-group">
  <label for="resourceSelect">* Resources</label>
  <div *ngIf="resourceList$ | async as resourceList; else loading">
     <ng-container *ngIf="resourceList.length; else noResults" >
        <div *ngFor="let r of resourceList; index as i">
          <!-- Show the result using checkboxes -->
        </div>
     </ng-container>
  </div>
</div>

<ng-template #loading>
  <br/>
  <div class="spinner-border spinner-border-sm text-muted" role="status">
    <span class="sr-only">Loading...</span>
  </div>
</ng-template>

<ng-template #noResults>
  <div class="text-muted">No results.</div>
</ng-template>

我不明白为什么使用this.service.getResources() 加载微调器没有显示,如果我使用this.service.fetchResourceList() 加载微调器显示正确。

我的目标是使用我提供的示例正确显示加载微调器,并保留我在组件中调用的方法。

【问题讨论】:

  • 这么简单的调用你的代码看起来太复杂了……你在发布之前清理了代码,还是完整的代码?
  • @Random 嗨,我删除了很多对我的问题不重要的东西。我在发布之前已经清理了它,这就是我今天使用的确切代码。它看起来很大,因为我正在实现本地缓存以将信息保存在内存中以避免将来不必要的 HTTP 请求。至少我是这么看的。
  • 您在控制台中看到任何错误吗?能否也确认一下http调用是否成功?
  • @KarthickManoharan 嗨,控制台中没有错误,我可以确认 HTTP 调用正确。请求的信息/结果正在显示,但加载微调器没有。
  • 感谢您的确认。您是否还可以通过限制速度来延迟响应时间来确保?因为如果 API 响应速度很快,那么微调器可能不会显示

标签: angular


【解决方案1】:

我花了时间在沙箱中重现它,但最终发现问题来自您的BehaviorSubject的初始化。 bsResource 的第一个值是 []。所以在你的*ngIf 中,当管道async 订阅它时,它会收到[] 的值,这是真的,所以*ngIf 立即为真,并且永远不会触发else(加载)块。

你所要做的就是用一个空值初始化你的BehaviorSubject(然后使用它修复代码,以保证空值安全):

private bsResource: BehaviorSubject<string[]> = new BehaviorSubject(null);

当你使用它时:

const cachedValue = this.bsResource.getValue();
if (!cachedValue || cachedValue.length === 0) {
  this.fetchResourceList().subscribe(res => this.bsResource.next(res));
}

我再说一遍,你不应该使用Promises,使用.subscribe,它的作用与toPromise().then相同。你永远不应该使用 Promise。 .toPromise 仅在升级 Angular 时有用,其中大部分代码使用 Promises,并且不想一次升级整个代码。

【讨论】:

  • 你说我需要用null 初始化BehaviorSubject 但是,然后你添加它用[] 初始化,我现在很困惑。另一件事是我认为您的if 语句内部存在逻辑问题,因为默认值为null,因此无法输入该语句。如果这将是应用程序第一次运行,那么它将永远不会进入该语句,因为它显然是null 而显然不是length === 0。另外,如果我们正在订阅,我们应该在保存结果后取消订阅,我错了吗?保持订阅状态不好。
  • @RRGT19 确实,我复制粘贴有点太快了。修复了 2 行代码 :) 另外,是的,您必须取消订阅(使用 takeUnit,或保存订阅的属性)
  • 如果我们订阅我们应该在保存结果后取消订阅,我错了吗?保持订阅开放是不好的。保存结果后可以退订吗?我不知道在这种情况下该怎么做才是正确的。
  • 是的,你可以。当然,我更喜欢完成 BehaviorSubject,或者使用 take(1) 或类似的东西,但它不是那么简单,必须检查它是否有效......最简单/最安全的方法确实是在订阅内取消订阅。但请注意,您将首先收到null,然后是实际值。所以你可以在订阅前添加一个filter管道,或者在订阅中添加一个if
  • 是的,.toObservable().pipe( filter(r =&gt; r !== null), take(1) ) 会成功的,我猜
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 1970-01-01
  • 2013-02-08
  • 1970-01-01
相关资源
最近更新 更多