0

In my Angular 11 project I have an Observable what changes the view and take some times while it's rendering.

Something like this:

export class MyComponent implements OnInit {
  myObservable = of([1, 2, 3]);

  constructor() { }

  ngOnInit(): void {
    this.myObservable.pipe(
      // here is some functions
    ).subscribe();
  }

}

After ngOnInit ran the observable still running and processing datas. After datas arrived the pipe change the template. But this change happening after ngAfterViewInit finished. So I need a soultion what run after pipe AND the rendering finished what is happening because pipe is finished.

How can I run codes (tooltip initialization) after my observable and the caused rendering is finished?

The ngAfterViewChecked not a good solution because it's always run when the view is changing, and my tooltip initialization will change the view too. So I want to run this initialization only once.

Is there any solution?

netdjw
  • 5,419
  • 21
  • 88
  • 162
  • You initialize your tool tip after each time your observable changes? – misha130 Jun 28 '21 at 17:34
  • the moment you rely on your data to be pre-loaded, your entire code becomes **asynchronous**, so work with asynchronous data using subscriptions. If you want to synchronise it, you might want to [Resolve](https://angular.io/api/router/Resolve) it – Aleksey Solovey Jun 28 '21 at 17:45
  • Have you looked at startWith operator of rxjs? – Fatih Ersoy Jun 28 '21 at 18:18
  • @misha130 Yes, because when it's changes the view changes too, disapper previous tooltip decorated elements and I need render new elements with new tooltip parameters. – netdjw Jun 28 '21 at 19:00
  • @AlekseySolovey I work with async datas and I use subscriptions, but still can't run tooltip initialization after view is re-rendered. – netdjw Jun 28 '21 at 19:01
  • @FatihErsoy how can startWith help on this problem? Could you present an example code please? Thx! – netdjw Jun 28 '21 at 19:02
  • Can you move the logic that takes a while to tap, map or mergeMap? After that add another tap to initialize the tooltip – misha130 Jun 28 '21 at 19:08
  • @misha130 the basic problem is the rendering happening after the subscribe finished. I want to run the initialization after rendering finished. map, tap not helped on this. setTimeout works with 1 sec delay, but it's not reactive and very instable solution, because network delay can be more than 1 sec... – netdjw Jun 29 '21 at 05:44
  • Well the problem is you also write that it happens after `ngAfterViewInit ` so its not "after rendering finished", its after rendering finished and then some other event happened after that. How about you make a specific component for this part and use `ngAfterViewInit` there so your other event will fire off this new component AfterViewInit? – misha130 Jun 29 '21 at 06:09
  • @misha130 the order of events is this: 1) ngOnInit starts the observables, 2) ngOnInit finished, but observable still running, 3) ngAfterViewInit finished, loading spinner is still on display, observable still running, 4) observable finished, 5) re-render view based on observable result, spinner disappear, tooltip fields are becoming visible. And here I need to run the initialization, but all this happening after the subscribe finished. Wierd. – netdjw Jun 29 '21 at 06:15

3 Answers3

0

You can use the async pipe to easily handle data coming from Observables, if you need to use the RxJS pipe function you can save the resulting Observable into a new variable and then use the async pipe on it.

Check this stackblitz example

Otherwise if the side effects in the pipe are really complex you can manually trigger Angular change-detection by using the ChangeDetectorRef. Check here for its documentation.

Thomas Iommi
  • 116
  • 5
0

you can use timout function as a workaround the page will load after pipe and after one second execute your code and you can decrease it to less than one second and you can determine it by trying :

export class MyComponent implements OnInit {
  myObservable = of([1, 2, 3]);
  constructor() { }

  ngOnInit(): void {
    this.myObservable.pipe(
      setTimeout( ()=>{
   // your code here
   }, 1000)
    ).subscribe();
  }

}
Asmaa Rashad
  • 593
  • 5
  • 28
  • setTimeout is not a good solution, because we can't predict how long is the process. Maybe 1 sec is too few, maybe too much. And obviously not reactive... – netdjw Jun 29 '21 at 05:40
0

I found a workaround:

I put all the code into a separated component what connected to the tooltip. Actually it's a label with an info icon. In this component I put the tooltip initialization code into the ngAfterViewInit function:

import { AfterViewInit, Component, Input } from '@angular/core';
import { TooltipService } from 'src/app/services/tooltip/tooltip.service';

@Component({
  selector: 'app-wizzard-label',
  templateUrl: './wizzard-label.component.html',
  styleUrls: ['./wizzard-label.component.scss'],
})
export class WizzardLabelComponent implements AfterViewInit {
  @Input() field: any = {};

  constructor(
    private tooltipService: TooltipService,
  ) { }

  ngAfterViewInit(): void {
    this.tooltipService.init();
  }
}

Now when the observable finish, disappear my spinner and show this component. The ngAfterViewInit do it's own job, and everything works fine.

netdjw
  • 5,419
  • 21
  • 88
  • 162