Capturing events emitted from components inside <ng-content>?

13,492

Solution 1

I ended up abandoning the event-driven approach and went with a shared service class instead.

Solution 2

You could get an instance of the login modal with @ContentChild() and manually subscribe to the open event

@Component({
    selector: 'modal-container',
    template: `
    <div [class]="css">
      <div [attr.id]="id" class="reveal" (open)="openModal()">
        <ng-content></ng-content>
      </div>
    </div>
  `
})
export class ModalContainerComponent {
    @ContentChild(LoginModalComponent)
    loginModal: LoginModalComponent;

    ngAfterViewInit() {
        this.loginModal.open.subscribe((event) => {
            //Handel event here
        });
    }
}

Solution 3

Just to expand on rob'a answer and comment

@ContentChild(LoginModalComponent) private models: QueryList<LoginModalComponent>;


ngAfterViewInit() {
    let models = this.models.toArray();

    for (let key in panels) {
        let model = models[key];
        panel.open.subscribe(()=>{
            // do things
        });

    }
}

This works in my case, and included for completeness.

Share:
13,492

Related videos on Youtube

Brandon Taylor
Author by

Brandon Taylor

I've been a professional web developer since 1996, using a broad range of technologies. My passion is creating web applications using Angular, JavaScript/TypeScript, Node and Python. I'm also a formally trained graphic designer and can handle full-stack projects from napkin-scribbling to finished product. I'm very enthusiastic, easy to get along and work with and love to help, teach and get others excited about web development and programming. In my spare time, I love to travel with my family, surf and teach martial arts.

Updated on October 09, 2022

Comments

  • Brandon Taylor
    Brandon Taylor over 1 year

    I have a custom modal component which uses <ng-content> to transclude content:

    @Component({
      selector: 'modal-container',
      template: `
        <div [class]="css">
          <div [attr.id]="id" class="reveal" (open)="openModal()">
            <ng-content></ng-content>
          </div>
        </div>
      `
    })
    export class ModalContainerComponent {
        . . .
    }
    

    In the contents of <ng-content> I have a component which emits the open event:

    @Component({
      selector: 'login-modal',
      template: `
        <modal-container [id]="'login-modal'">
          <section>...</section>
        </modal-container>
      `,
    })
    export class LoginModalComponent implements OnInit {
    
        @Output()
        open = new EventEmitter();
    
        ngOnInit(): void {
            // Here I am checking an ngrx store with code that is not included
            if (state.openLoginModal) {
              this.open.emit();
            }
        }
    
    }
    

    however, the ModalContainerComponent never receives the event.

    Examples such as:

    are coming up short. What am I doing wrong?


    Update:

    Since @Output events don't bubble, I decided to go with a custom directive to emit the event:

    import { Directive, ElementRef, Renderer } from '@angular/core';
    
    
    @Directive({
      selector: '[open-modal]',
      host: { '(click)': 'openModal()' }
    })
    export class OpenModalDirective {
    
      constructor(
        private elementRef: ElementRef,
        private renderer: Renderer
      ) {}
    
      openModal(): void {
        this.renderer.invokeElementMethod(this.elementRef.nativeElement,
          'dispatchEvent',
          [new CustomEvent('open-modal-container', { bubbles: true })]);
      }
    
    }
    

    using: in Angular2 how to know when ANY form input field lost focus as an example.

    However, I still can't pick up the custom event in ModalContainerComponent:

    @HostListener('open-modal-container')
    openModalContainer(): void {
      console.log('openModal() was invoked');
    }
    

    I can log the click event, so that's happening, but the host listener is failing. Thoughts?


    Update 2

    I'm abandoning this approach in favor of a shared service, but I'm running into an issue with .next() not providing a value to the subscriber: Subscriber doesn't receive value from .next()