如何订阅通过@angular/cdk/portal 注入的组件的 Observable? [英] How to subscribe to Observable of Component injected via @angular/cdk/portal?
问题描述
我正在尝试实现一个基本的(非常基本的)模态实现.我有一个 ModalService
和一个 ModalComponent
.
ModalService
创建一个 ModalComponent
的实例,并使用@angular/cdk/portal 将其注入到页面中.
我可以让模态显示得很好:-)
ModalComponent
有一个 Observable 属性,我希望 ModalService
订阅它,这样当在模态中单击关闭"按钮时,可以发出一个值并且 ModalService
可以关闭模态.
但是,当组件发出值时,Service 不会对其进行操作.从服务端看,我好像订阅了,但是从组件端看,却显示0个观察者.
我想也许我可以使用典型的 @Output()
EventEmitter,但我不确定在模态类中将其连接起来,因为最初不存在子元素.
我在想我的组件引用可能不太正确(也许我有 2 个不同的组件?).我正在尝试来自
点击按钮,模态出现.它看起来不像模态,但这可能是因为缺少样式:
现在点击模态内的关闭按钮:
您会看到模态消失了,控制台输出显示了您的消息.
这篇文章是否能帮助您找到错过的花絮?
<小时>补充一点.如果您想将数据从模态发送到调用它的组件,请更改 modalComponent 的 closeModal 方法:
closeModal() {//console.log(this.onModalClose()) **显示零观察者** :-(this.modalClose.next('你的数据在这里,如果需要它可以是一个对象');this.modalClose.complete();}
并且您的模态服务可以访问 onModalClose().subscribe()
方法中的数据:
componentRef.instance.onModalClose().subscribe((results) => {控制台日志(结果);console.log('如果这个被调用,我会很高兴,但它从来没有');this.closeModal();});
I'm trying to implement a basic (very basic) modal implementation. I've got a ModalService
and a ModalComponent
.
The ModalService
creates an instance of the ModalComponent
and injects it into the page using the @angular/cdk/portal.
I can get the modal to display just fine :-)
The ModalComponent
has an Observable property that I want the ModalService
to subscribe to so that when the 'close' button is clicked within the modal, a value can be emitted and the ModalService
can close the modal.
However, when the component emits the value, the Service is not acting on it. From the Service side, it looks like I'm subscribed, but from the Component side, it shows 0 observers.
I thought maybe I could use a typical @Output()
EventEmitter, but I'm not sure to hook that up since the in the modal class, since the child element doesn't exist initially.
I'm thinking maybe my component reference is not quite right (maybe I have 2 different ones?). I'm trying suggestion from this answer
Any ideas what I'm doing wrong?
Service
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();
}
}
Component
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();
}
}
Also, if I happen to be asking the wrong question, meaning there's a better overall approach, I'm open to suggestions. :-)
Thanks!
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<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();
}
}
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<any> = new Subject();
onModalClose(): Observable<any> {
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:
<p>
modal works!
</p>
<button (click)="closeModal()">Close Modal</button>
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:
<button (click)="showModal()">Show Modal</button>
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();
});
这篇关于如何订阅通过@angular/cdk/portal 注入的组件的 Observable?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!