await Angular's EventEmitter
Asked Answered
P

3

10

Is it possible to await till Angular EventEmitter emits, like it's possible to await ajax call (Angular 5 synchronous HTTP call)?

What I want to do is:

Having a Child component

@Component({
  template: '<button (click)="emit1()">emit</button>'
})
export class ChildComponent {
  @Output() dataChanged: EventEmitter<number> = new EventEmitter<number>(true);  // synchronous

  emit1(){
    this.dataChanged.emit(1)
  }
}

And a Parent component that dynamically creates a Child component

@Component()
export class ParentComponent {
  childComponentRef: ComponentRef<ChildComponent>

  constructor(private resolver: ComponentFactoryResolver, private vcRef: ViewContainerRef) {}

  openChild() {
    const factory = this.resolver.resolveComponentFactory(ChildComponent);
    this.childComponentRef = this.vcRef.createComponent(factory);
    await this.childComponentRef.instance.dataChanged.toPromise()
    alert('emited!')
  }

}

I want to (a)wait until the value is emitted from a Child component.

Peony answered 7/6, 2020 at 19:16 Comment(1)
EventEmitters are things you listen to.. they publish things. So instead of doing an 'await' you would simply want to do something when they emit data.Denaedenarius
D
1

You can use the firstValueFrom function from rxjs.

Converts an observable to a promise by subscribing to the observable, and returning a promise that will resolve as soon as the first value arrives from the observable. The subscription will then be closed.

It turns everithing that is subscriptable in a promise. Will be something like this:

import { firstValueFrom } from 'rxjs';
@Component()
export class ParentComponent {
  childComponentRef: ComponentRef < ChildComponent > ;

  constructor(
    private resolver: ComponentFactoryResolver,
    private vcRef: ViewContainerRef
  ) {}

  openChild() {
    const factory = this.resolver.resolveComponentFactory(ChildComponent);
    this.childComponentRef = this.vcRef.createComponent(factory);
    await firstValueFrom(this.childComponentRef.instance.dataChanged).then(() => {
      alert('emited!');
    })

  }
}
Diametral answered 8/12, 2023 at 19:55 Comment(1)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Gosh
S
0

Angular EventEmitter is an interface extension of RxJS Subject. They are infact asynchronous and not synchronous like mentioned in the question. And so you need to subscribe to it listen to the emitted notifications.

openChild() {
  const factory = this.resolver.resolveComponentFactory(ChildComponent);
  this.childComponentRef = this.vcRef.createComponent(factory);
  this.childComponentRef.instance.dataChanged.subscribe(
    response => { alert('emited!') }
  );
}
Standin answered 7/6, 2020 at 19:32 Comment(3)
I understand the usual way of using subscribe on EventEmiters, but what I'd like to know if I can await them in a similar way an Ajax call can be awaited: https://mcmap.net/q/731255/-angular-5-synchronous-http-call/3569205Peony
take a look at EventEmitter's constructor(isAsync: boolean = false). That's what I'm using and that's what I'm referring to as synchronous.Peony
I can't go further on the async/await conversion of the observable. But I believe the argument should actually be false to make the EventEmitter synchronous. From source: [isAsync=false] When true, deliver events asynchronously.Standin
P
0

You could pipe the dataChanged with first() to await the first value of the EventEmitter:

      openChild() {
        ...
        await this.childComponentRef.instance.dataChanged.pipe(first()).toPromise()
        alert('emited!')
      }
Polymorphonuclear answered 8/8, 2022 at 8:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.