How to subscribe to Observable of Component injected via @angular/cdk/portal?

后端 未结 1 1392
Happy的楠姐
Happy的楠姐 2021-01-06 05:04

I\'m trying to implement a basic (very basic) modal implementation. I\'ve got a ModalService and a ModalComponent.

The ModalServic

1条回答
  •  Happy的楠姐
    2021-01-06 05:32

    This code works for me with very few changes, so I'm confused as to what your problem is. First I created a project with the Angular CLI using ng new. Then I installed ng material using the instructions on their site.

    I created a modal service which is identical to yours:

        import {ApplicationRef, ComponentFactoryResolver, Injectable, Injector} from '@angular/core';
    import {DomPortalHost, ComponentPortal} from "@angular/cdk/portal";
    import {ModalComponent} from "./modal/modal.component";
    
    @Injectable({
      providedIn: 'root'
    })
    export class ModalService {
    
      private modalPortal: ComponentPortal;
      private bodyPortalHost: DomPortalHost;
    
      constructor(private componentFactoryResolver: ComponentFactoryResolver,
                  private appRef: ApplicationRef,
                  private injector: Injector) {
      }
    
      showModal(modalName: string) {
    
        // set up the portal and portal host
        this.modalPortal = new ComponentPortal(ModalComponent);
        this.bodyPortalHost = new DomPortalHost(
          document.body,
          this.componentFactoryResolver,
          this.appRef,
          this.injector);
    
        // display the component in the page
        let componentRef = this.bodyPortalHost.attach(this.modalPortal);
    
    
        // listen for modal's close event
        componentRef.instance.onModalClose().subscribe(() => {
          console.log('I will be very happy if this this gets called, but it never ever does');
          this.closeModal();
        });
    
        // console.log(componentRef.instance.onModalClose()) shows 1 subscriber.
      }
    
      closeModal() {
        this.bodyPortalHost.detach();
      }
    }
    

    I created a modal component. The TypeScript is the same as yours:

    import { Component, OnInit } from '@angular/core';
    import {Observable, Subject} from "rxjs/index";
    
    @Component({
      selector: 'app-modal',
      templateUrl: './modal.component.html',
      styleUrls: ['./modal.component.css']
    })
    export class ModalComponent implements OnInit {
    
      constructor() { }
    
      ngOnInit() {
      }
    
      private modalClose: Subject = new Subject();
    
      onModalClose(): Observable {
        return this.modalClose.asObservable();
      }
    
      closeModal() {
        // console.log(this.onModalClose()) **shows zero observers**   :-(
        this.modalClose.next();
        this.modalClose.complete();
      }
    
    }
    

    You didn't give us a model for the HTML, but this is what I used:

    modal works!

    Here is my app.module.ts:

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
    
    import { AppComponent } from './app.component';
    import {ModalService} from "src/app/modal.service";
    import { ModalComponent } from './modal/modal.component';
    
    @NgModule({
      declarations: [
        AppComponent,
        ModalComponent
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule
      ],
      providers: [ModalService],
      bootstrap: [AppComponent],
      entryComponents: [ModalComponent]
    })
    export class AppModule { }
    

    Note I Defined the ModalComponent in the entryComponents and the ModalService as a provider.

    The app.component HTML:

    
    

    And the app.component typescript:

    import { Component } from '@angular/core';
    import {ModalService} from "./modal.service";
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'app';
    
      constructor(public modalService: ModalService){}
    
      showModal(){
           this.modalService.showModal("This is the modal name");
      }
    
    }
    

    I injected the ModalService into the constructor and called your showModal method in response to the button click in the main app.

    Load the app:

    Click the button and the modal shows up. It doesn't look like a modal, but that could be because of missing styling :

    Now click the close button inside the modal:

    You see the modal is gone and the console output displays your message.

    Does this post help you find the tidbit you missed?


    One thing to add. If you want to send data out of your modal to the component that called it, ust change your modalComponent's closeModal method:

      closeModal() {
        // console.log(this.onModalClose()) **shows zero observers**   :-(
        this.modalClose.next('Your Data Here, it can be an object if need be');
        this.modalClose.complete();
      }
    

    And your modal service can access the data in the onModalClose().subscribe() method:

    componentRef.instance.onModalClose().subscribe((results) => {
      console.log(results);
      console.log('I will be very happy if this this gets called, but it never ever does');
      this.closeModal();
    });
    

    0 讨论(0)
提交回复
热议问题