如何订阅通过@angular/cdk/portal注入的组件的可观察性?

如何订阅通过@angular/cdk/portal注入的组件的可观察性?,angular,rxjs,angular-cdk,Angular,Rxjs,Angular Cdk,我正在尝试实现一个基本的(非常基本的)模式实现。我有一个ModalService和一个ModalComponent ModalService创建ModalComponent的实例,并使用@angular/cdk/portal将其注入页面 我可以让模态显示得很好:-) ModalComponent有一个可观察的属性,我希望ModalService订阅该属性,以便在模式中单击“关闭”按钮时,可以发出一个值,并且ModalService可以关闭模式 但是,当组件发出值时,服务不会对其进行操作。从服务端

我正在尝试实现一个基本的(非常基本的)模式实现。我有一个
ModalService
和一个
ModalComponent

ModalService
创建
ModalComponent
的实例,并使用@angular/cdk/portal将其注入页面

我可以让模态显示得很好:-)

ModalComponent
有一个可观察的属性,我希望
ModalService
订阅该属性,以便在模式中单击“关闭”按钮时,可以发出一个值,并且
ModalService
可以关闭模式

但是,当组件发出值时,服务不会对其进行操作。从服务端看,似乎我已订阅,但从组件端看,它显示0个观察者

我想也许我可以使用一个典型的
@Output()
EventEmitter,但我不确定是否要连接它,因为在模态类中,子元素最初并不存在

我在想也许我的组件参考不太正确(也许我有两个不同的?)。我想听听你的建议

知道我做错了什么吗

服务

export class ModalService {

    private modalPortal: ComponentPortal<any>;
    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();
    }
}
导出类ModalService{
私有模式门户:组件门户;
私人bodyPortalHost:DomPortalHost;
构造函数(专用componentFactoryResolver:componentFactoryResolver,
私有appRef:ApplicationRef,
专用注射器(注射器){
}
showModal(modalName:string){
//设置门户和门户主机
this.modalPortal=新组件门户(ModalComponent);
this.bodyPortalHost=新的DomPortalHost(
文件正文,
此.componentFactoryResolver,
这个.appRef,,
这是一种新的方法(喷油器);
//在页面中显示组件
让componentRef=this.bodyPortalHost.attach(this.modalPortal);
//倾听莫代尔的结束事件
componentRef.instance.onModalClose().subscribe(()=>{
log('如果调用它,我会非常高兴,但它永远不会这样做');
这个.closeModal();
});
//log(componentRef.instance.onModalClose())显示1个订阅者。
}
closeModal(){
此.bodyPortalHost.detach();
}
}
组件

export class ModalComponent {

    private modalClose: Subject<any> = new Subject();

    onModalClose(): Observable<any> {
        return this.modalClose.asObservable();
    }

    closeModal() {
        // console.log(this.onModalClose()) **shows zero observers**   :-(
        this.modalClose.next();
        this.modalClose.complete();
    }
}
导出类ModalComponent{
private modalClose:Subject=新Subject();
onModalClose():可观察{
返回此.modalClose.asObservable();
}
closeModal(){
//console.log(this.onModalClose())**显示零个观察者**:-(
this.modalClose.next();
this.modalClose.complete();
}
}
此外,如果我碰巧问了一个错误的问题,这意味着有一个更好的整体方法,我愿意接受建议。:-)


谢谢

这段代码对我来说只需要很少的修改,所以我不清楚您的问题是什么。首先,我使用angularcli创建了一个项目,使用
ngnew
。然后,我安装了ng材料使用

我创建了一个与您相同的模式服务:

    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<any>;
  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();
  }
}
注:我在entryComponents中定义了ModalComponent,并将ModalService定义为提供者

app.component HTML:

<button (click)="showModal()">Show Modal</button>
我将ModalService注入构造函数,并调用showModal方法来响应主应用程序中的按钮单击

加载应用程序:

单击按钮,模态显示出来。它看起来不像模态,但这可能是因为缺少样式:

现在,单击模式内的“关闭”按钮:

您可以看到模式消失,控制台输出显示您的消息

这篇文章能帮你找到你错过的小道消息吗


还有一件事要补充。如果要将数据从模态发送到调用它的组件,必须更改modalComponent的closeModal方法:

  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();
  }
您的模式服务可以通过
onModalClose().subscribe()方法访问数据:

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();
});

您应该附加
this
,以指示该属性是类的一部分。(又称
console.log(onModalClose())
应该是
console.log(this.onModalClose())
谢谢@Edric的评论,但实际上我使用的是
这个
,我只是错误地从我的问题中省略了它。我更新了上面的代码。请检查一下这个示例,看起来它工作正常。哇!我似乎正在那里工作。同样的代码是吗?现在我必须弄清楚为什么这不能在本地工作。!嘿@Jeffryhouse,th我想你在对这个问题的评论中没有注意到,@yurzui指出我的代码已经在运行,我对修复进行了评论。之所以发生这种情况,是因为我在这篇文章中的“最小示例”比我的原始代码更简单,在某种程度上使它真正起作用。无论如何,感谢你为我整理这些代码所做的工作为找到此问题的未来访客提供的代码和屏幕截图!奖金授予:-)另外,很高兴您指出了
entryComponents
,因为这是必需的,我将提到我在最初的尝试中了解到了它,因为我最初没有配置它,但是Angular有很棒的控制台日志记录,特别提到了添加它,GoAngular团队@直到我写了这篇文章之后,我才在评论中注意到这一点。我希望我先读一读;因为这样可以节省我一些时间。谢谢你的赏金。我很高兴你们都准备好了;希望这篇文章将来能对其他人有所帮助。
<button (click)="showModal()">Show Modal</button>
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");
  }

}
  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();
  }
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();
});