1

I use component on my html template with quite complicated *ngIf condition

<my-component #myComponent *ngIf="..."></my-component>

and in .ts file

@ViewChild('myComponent') public myComponent: MyComponent;

After 'click' on button I send request, show/hide some stuff... on ma page - and after that *ngIf in my component should be set to true. When this.myComponent is not null I can call init(data) function inside it. I try to use rxjs for "wait" when this component is not null (setTimeout approach fails sometimes) as follows

let checking = true;

let data = await this.loadData();

interval(100).pipe(takeWhile(()=> checking)).subscribe( _ => { 
  if(this.myComponent) { 
    this.myComponent.init(data)
    checking = false;
  }
})

// ... (stuff which magic which makes that this.myComponent is not null)

But this code is quite not nice - it checks variable value every 100ms, the code is also quite long and complicated. Is there a more smart solution which e.g. which trigger code exactly only when this.myComponent change value from null (without pinging it every 100ms)?

Update

I forgot to mention than above interval... code is inside some async callback so actually I need to wait when data is ready and myComponent is not null ( I update above code too)

Kamil Kiełczewski
  • 71,169
  • 26
  • 324
  • 295

2 Answers2

2

You should move the call this.myComponent.init(data) inside of ngOnInit within MyComponent. Use an @Input() variable to initialize the component with Data.

<my-component #myComponent *ngIf="..." [data]="data"></my-component>

And leverage ngOnInit inside of MyComponent to do the initialization:

@NgComponent({
  selector: 'my-component'
})
export class MyComponent implements OnInit {
   @Input()
   data: Data;

   ngOnInit() {
      if(this.data) {
         this.init(data);
      }
   }
   init(data: Data) {
      ...
   }
}

ngOnInit is guaranteed to be called once per compoonent lifetime. And ngIf will destroy/create the component each time boolean expression is toggled.

pixelbits
  • 50,631
  • 15
  • 93
  • 132
  • this is quite clever approach :) - however the `data` is inside quite nested async code - so actually I must wait for it too ( I update question) - but I give +1 - thank you – Kamil Kiełczewski Oct 27 '20 at 20:44
2

You could use an EventEmitter on the child component and listen for that:

On the child component, which you send in its ngOnInit function or whenever you're ready for it:

@Output() loaded = new EventEmitter<void>();
await this.getSomeDataFromSomeWhere();
this.loaded.emit();

In the parent component HTML:

<my-component #myComponent *ngIf="..." (loaded)="myComponent.init(data)"></my-component>
Ruben Helsloot
  • 11,812
  • 5
  • 19
  • 40
  • this is quite clever approach - however on my-component I do ngOnInit() { loaded.emit() } - and in parent html (load)=initMyComponent() and in .ts initMyComponent() { this.myComponent.init(this.data) } - however the data is inside quite nested async code - so actually I must wait for it too. But I give +1 - thank you – Kamil Kiełczewski Oct 27 '20 at 20:45