使用settimeout的角度延迟视图更新

发布于 2025-02-04 03:16:36 字数 1577 浏览 1 评论 0原文

由于应用程序数据库更新如此之快,因此我们添加了一个物质进度栏,以帮助视觉提示,以表明发生了一些事情。

我们的逻辑是,我们将进度栏设置为最小延迟500ms。我们将表演加载设置为启动进度键,然后执行任务,然后致电HideloDADING关闭ProgressBar。如果最小延迟被超过,则该进度键停止,如果没有,它将等到最小延迟时间过时,而Settimeout将负责停止。要明显这部分正在起作用。

我们遇到的问题是,当我们执行任务并要更新UI IE时。将结果显示在表中,直到SettiMeout完成为止。

我已经将console.log放在适当的位置,任务正在运行,返回结果是purley,视图没有更新。

加载程序服务

export class LoaderService {

private _isLoading = false;
private _inProgress = false;
private _delayFinished = false;

private minimumProgressDelay: number = 200;

public get isLoading() {
    return this._isLoading;
}

public showLoading(delay?: number) {
    this._isLoading = true;
    this._inProgress = true;
    this._delayFinished = true;
    delay = delay ? delay : this.minimumProgressDelay;

    setTimeout(() => {
        this._delayFinished = false;
        if (!this._inProgress) {
            this._isLoading = false;
        }
    }, delay);
}

public hideLoading() {
    this._inProgress = false;
    if (!this._delayFinished) {
        this._isLoading = false;
    }
}

}

eNcers bar in header组件中

<div *ngIf="showLoading">
  <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>

的典型使用加载器

async search(): Promise<void> {
    this.uiService.loader.showLoading(500);

    await this.getResults();

    this.uiService.loader.hideLoading();
}

getResults函数基本上执行异步HTTPRequest以获取结果,然后设置表的数据属性。

如前所述,代码所有作品excpet excpet在延迟完成之前,对表的更新将不会更新。例如。如果我们将其设置为10000ms,则需要等到10秒钟显示结果。

Due to the apps database updates being so quick we have added a material progress bar to help with visual cues to indicate that something has happened.

Our logic is that we set the progressbar to a minimum delay of say 500ms. We set the showLoading to start the progressbar and we then perform the task and then call hideLoading to turn off the progressbar. If the minimum delay is surpassed the progressbar stops if not it will wait until the minimum delay time has elapsed and the setTimeout will take care of the stop. To be clear this part is working.

The issue we have is that when we perform the task and we want to update the ui ie. display the results in a table it doesnt update until the setTimeout has completed.

I have put console.log(s) in the appropriate places and the task is running and returning results it is purley the view is not updating.

Loader Service

export class LoaderService {

private _isLoading = false;
private _inProgress = false;
private _delayFinished = false;

private minimumProgressDelay: number = 200;

public get isLoading() {
    return this._isLoading;
}

public showLoading(delay?: number) {
    this._isLoading = true;
    this._inProgress = true;
    this._delayFinished = true;
    delay = delay ? delay : this.minimumProgressDelay;

    setTimeout(() => {
        this._delayFinished = false;
        if (!this._inProgress) {
            this._isLoading = false;
        }
    }, delay);
}

public hideLoading() {
    this._inProgress = false;
    if (!this._delayFinished) {
        this._isLoading = false;
    }
}

}

Progress Bar in Header component

<div *ngIf="showLoading">
  <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>

Typical use of the loader

async search(): Promise<void> {
    this.uiService.loader.showLoading(500);

    await this.getResults();

    this.uiService.loader.hideLoading();
}

The getResults function basically does an async httprequest to get the results and then sets the data property for the table.

As previously explained the code all works excpet the update to the table is not updated until the delay is completed. eg. if we set it to 10000ms it does wait until the 10secs to show the results.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夜未央樱花落 2025-02-11 03:16:36

您的加载服务

我对您要做的事情有些困惑,但我会尽力解释发生了什么。您说您不希望该服务在继续前进之前等待10秒。但是,您会说出10秒钟的超时服务似乎有意使用户无缘无故地等待。

您是否是指使用setInterval而不是settimeout?在这种情况下,您可能需要使用计时器,它允许您同时设置初始延迟和间隔时间。但是即使那样,我也不了解您的代码。似乎您并没有试图将其投票到后端服务的任何API的终点。

正如Drenai所指出的那样,这是正确的,看起来您正在尝试创建一种非常缓慢观察服务中一些变量的方法。我看到的方式在您的LoadingService中没有任何逻辑,除了可能存储布尔值之外。但是,您可以在组件本身中这样做。

这种代码的平静已经使它已经使它等待,因此将加载到false之前等待,这使得服务中的逻辑是多余的:

await this.getResults();

您可以将这些布尔值推到capisy> civation usibject,并且观察来自任何地方如果那是您要完成的工作。

在服务中:

export class LoadingService {
  private loadingSubject = new BehaviorSubject(false);

  get loading$() {
    return this.loadingSubject.asObservable();
  }

  showLoading() {
    this.loadingSubject.next(true)
  }

  hideLoading() {
    this.loadingSubject.next(false)
  }
}

要观察组件中的布尔值:

ngOnInit() {
  this.loadingService.loading$.subscribe(
    value => {
      this.loading = value
    }
  )
}

我创建了一个 stackblitz 表明它只是有效的。也许我理解您错了,您可以将其用作创建问题最小再现的起点。

进行轮询

,以防您试图从后端获取数据,请检查此有关轮询的问题并使用计时器来做到这一点。

解析器服务

以防您想在路由时获取数据,我会建议使用解析器。

您制作一个调用您服务的解析器:

export class DataResolverService implements Resolve<DataResolved> {
  
  constructor(
    private dataService: DataService
  ) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): DataResolved | Observable<DataResolved> | Promise<DataResolved> {
    return this.dataService.getData()
    .pipe(
      map(data => ({ data })),
      catchError(error => {
        const message = `Retrieval error: ${error}`
        console.error(message)
        return of({ data: [], error: message } as DataResolved)
      })
    )
  }
}

示例数据服务:

export class DataService {
  private dataUrl = 'api/data';

  constructor(private http: HttpClient) {}

  getData() {
    return this.http.get<Data[]>(this.dataUrl);
  }
}

然后将解析器添加到路线上:

{
  path: '',
  component: SomeComponent,
  resolve: { resolvedData: DataResolverService },
}

在组件中,您可以从路由器快照中获取上面描述的ResolvedData,或者您可以订阅该路由的数据

export class SomeComponent implements OnInit {
  data: Data[];

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.data = this.route.snapshot.data['resolvedData'].data;
  }
}

:何时显示旋转器,您现在可以订阅路由器的事件:

export class AppComponent implements OnInit {
  loading = true;

  constructor(private router: Router) {}

  ngOnInit() {
    this.router.events.subscribe((routerEvent: Event) => {
      this.checkRouterEvent(routerEvent);
    });
  }

  checkRouterEvent(routerEvent: Event): void {
    if (routerEvent instanceof NavigationStart) {
      this.loading = true;
    } else if (
      routerEvent instanceof NavigationEnd ||
      routerEvent instanceof NavigationCancel ||
      routerEvent instanceof NavigationError
    ) {
      this.loading = false;
    }
  }
}

这是

Your Loading Service

I'm a little confused about what you are trying to do, but I'll give it my best shot to explain what is going on. You say you don't want the service to wait for 10s before moving on. But you litterally tell your service to timeout for 10s seemingly intentionally making the user wait for no reason.

Did you perhaps mean to use setInterval instead of setTimeout? In that case you might want to use timer instead, it allows you to set both an initial delay and a interval time. But even then I do not understand your code. It doesn't seem like you are trying to poll to the endpoint of any API of a back end service.

As Drenai points out, and rightly so, it looks like you are trying to create a way to very slowly observe some variable in a service. The way I see it none of the logic in your LoadingService is very useful except for maybe storing a boolean. But, you could just do that in the component itself.

This peace of code already makes it so the application will wait before setting loading back to false, which kind of makes the logic in the service redundant:

await this.getResults();

You could push these booleans to a BehaviorSubject and observe is from anywhere if that is what you are trying to accomplish.

In the service:

export class LoadingService {
  private loadingSubject = new BehaviorSubject(false);

  get loading$() {
    return this.loadingSubject.asObservable();
  }

  showLoading() {
    this.loadingSubject.next(true)
  }

  hideLoading() {
    this.loadingSubject.next(false)
  }
}

To observe the boolean in your component:

ngOnInit() {
  this.loadingService.loading$.subscribe(
    value => {
      this.loading = value
    }
  )
}

I created a Stackblitz to show it just works. Maybe I understood you wrong and you can use this as a startingpoint to create a minimal reproduction of your problem.

Polling

In case you ARE trying to get data from a back end, check this question about polling and using timer to do so.

Resolver Service

In case you wanted to grab data when routing I would advice using a Resolver.

You make a resolver that calls your service:

export class DataResolverService implements Resolve<DataResolved> {
  
  constructor(
    private dataService: DataService
  ) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): DataResolved | Observable<DataResolved> | Promise<DataResolved> {
    return this.dataService.getData()
    .pipe(
      map(data => ({ data })),
      catchError(error => {
        const message = `Retrieval error: ${error}`
        console.error(message)
        return of({ data: [], error: message } as DataResolved)
      })
    )
  }
}

An example data service:

export class DataService {
  private dataUrl = 'api/data';

  constructor(private http: HttpClient) {}

  getData() {
    return this.http.get<Data[]>(this.dataUrl);
  }
}

Then you add the resolver to your route:

{
  path: '',
  component: SomeComponent,
  resolve: { resolvedData: DataResolverService },
}

In your component you can now either get the resolvedData described above from a router snapshot or you can subscribe to the route's data:

export class SomeComponent implements OnInit {
  data: Data[];

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.data = this.route.snapshot.data['resolvedData'].data;
  }
}

To know when to show a spinner you can now just subscribe to your router's events:

export class AppComponent implements OnInit {
  loading = true;

  constructor(private router: Router) {}

  ngOnInit() {
    this.router.events.subscribe((routerEvent: Event) => {
      this.checkRouterEvent(routerEvent);
    });
  }

  checkRouterEvent(routerEvent: Event): void {
    if (routerEvent instanceof NavigationStart) {
      this.loading = true;
    } else if (
      routerEvent instanceof NavigationEnd ||
      routerEvent instanceof NavigationCancel ||
      routerEvent instanceof NavigationError
    ) {
      this.loading = false;
    }
  }
}

Here's the full example on Stackbitz of this as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文