@Component({ selector : 'modal-dialog', ... }) export class ModalDialogComponent { @Input() header: string; @Input() description: string; @Output() isConfirmed: EventEmitter<boolean> = new EventEmitter<boolean>(); private confirm() { this.isConfirmed.emit(true); } private close() { this.isConfirmed.emit(false); } }
export class ModalDialogResult { public isConfirmed: boolean; public data:any; }
@NgModule({ imports : [BrowserModule], declarations: [SimpleModalPageComponent, ModalDialogComponent], bootstrap : [SimpleModalPageComponent] }) export class SimpleModalPageModule { }
<div class="container"> <div class="configuration"> <div> <label for="header">:</label> <input type="text" id="header" #header class="simple-input"> </div> <label for="description">:</label> <input type="text" id="description" #description content="description" class="simple-input"> </div> <div> <button class="simple-button" (click)="showDialog()">Show Dialog</button> </div> </div> <modal-dialog *ngIf="isModalDialogVisible" [header]="header.value" [description]="description.value" (isConfirmed)="closeModal($event)"></modal-dialog>
.... export class SimpleModalPageComponent { private isModalDialogVisible: boolean = false; public showDialog() { this.isModalDialogVisible = true; } public closeModal(isConfirmed: boolean) { this.isModalDialogVisible = false; ... } }
@Injectable() export class TinyNotificationService { private notifications: Subject<TinyNotificationModel> = new Subject<TinyNotificationModel>(); public getNotifications(): Subject<TinyNotificationModel> { return this.notifications; } public showToast(info: TinyNotificationModel) { this.notifications.next(info); } } export class TinyNotificationModel { public header: string; public description: string; constructor(header: string, description: string) { this.header = header; this.description = description; } }
@Component({ selector : "notifications", template : ` <div class="tiny-notification-panel"> <div *ngFor="let notification of notifications" class="tiny-notification"> <div class="header-block"> <h3 class="header-title">{{notification.header}}</h3> <a class="close-button" (click)="closeNotification(notification)">x</a> </div> <div class="content"> <span>{{notification.description}}</span> </div> </div> </div>` }) export class TinyNotificationComponent { notifications: Set<TinyNotificationModel> = new Set<TinyNotificationModel>(); constructor(private _notificationService: TinyNotificationService) { this._notificationService.getNotification() .subscribe((notification: TinyNotificationModel)=> { this.notifications.add(notification); setTimeout(()=> { this.closeNotification(notification); }, 5000); }); } public closeNotification(notification: TinyNotificationModel) { this.notifications.delete(notification); } }
<notifications></notifications>
... constructor(private notificationService: TinyNotificationService) {} public showToast(header: string, description: string) { this.notificationService.showToast(new TinyNotificationModel(header, description)); } ...
@Component({ ... template: ` ... <section #notificationBlock></section> `, ... }) export class DynamicModalPageComponent implements OnInit { @ViewChild('notificationBlock', { read: ViewContainerRef }) notificationBlock: ViewContainerRef; constructor(private notificationManager: NotificationManager) { } public ngOnInit(): void { this.notificationManager.init(this.notificationBlock); } .. }
@Injectable() export class NotificationManager { private notificationBlock: ViewContainerRef; ... constructor(private componentFactoryResolver: ComponentFactoryResolver) { } public init(notificationBlock: ViewContainerRef) { this.notificationBlock = notificationBlock; ... } ... private createComponent<T>(componentType: {new (...args: any[]): T;}): ComponentRef<T> { const injector = ReflectiveInjector.fromResolvedProviders([], this.notificationBlock.parentInjector); const factory = this.componentFactoryResolver.resolveComponentFactory(componentType); return factory.create(injector); } private createNotificationWithData<T>(componentType: {new (...args: any[]): T;}, data: any): ComponentRef<T> { const component = this.createComponent(componentType); Object.assign(component.instance, data); return component; } }
private createComponent<T>(componentType: {new (...args: any[]): T;}): ComponentRef<T> { const injector = ReflectiveInjector.fromResolvedProviders([], this.notificationBlock.parentInjector); const factory = this.componentFactoryResolver.resolveComponentFactory(componentType); return factory.create(injector); }
private createNotificationWithData<T>(componentType: {new (...args: any[]): T;}, data: any): ComponentRef<T> { const component = this.createComponent(componentType); Object.assign(component.instance, data); return component; }
@Injectable() export class NotificationManager { ... public showDialog<T extends ModalDialogBase>(componentType: {new (...args: any[]): T;}, header: string, description: string): Subject<ModalDialogResult> { const dialog = this.createNotificationWithData(componentType, { header : header, description: description }); this.notificationBlock.insert(dialog.hostView); const subject = dialog.instance.getDialogState(); const sub = subject.subscribe(x=> { dialog.destroy(); sub.unsubscribe(); }); return subject; } ... }
export abstract class ModalDialogBase { public abstract getDialogState(): Subject<ModalDialogResult>; } export enum ModalDialogResult{ Opened, Confirmed, Closed }
this.notificationBlock.insert(dialog.hostView);
const subject = dialog.instance.getDialogState(); const sub = subject.subscribe(x=> { dialog.destroy(); sub.unsubscribe(); });
@Component({ selector : 'modal-dialog', template : ` <div class="modal-background"> <div class="container"> <div class="header-block"> <h3 class="header-title">{{header}}</h3> <a class="close-button" (click)="close()">x</a> </div> <div class="content"> <span>{{description}}</span> </div> <div class="action-block"> <button class="simple-button" (click)="confirm()"></button> <button class="simple-button" (click)="close()"></button> </div> </div> </div> ` }) export class ModalDialogComponent extends ModalDialogBase { private header: string; private description: string; private modalState: Subject<ModalDialogResult>; constructor() { super(); this.modalState = new Subject(); } public getDialogState(): Subject<ModalDialogResult> { return this.modalState; } private confirm() { this.modalState.next(ModalDialogResult.Confirmed); } private close() { this.modalState.next(ModalDialogResult.Closed); } }
@Component({ selector : 'notification-panel', template : ` <div class="notification-panel"> <div #notifications></div> </div> }) export class NotificationPanelComponent { @ViewChild('notifications', { read: ViewContainerRef }) notificationBlock: ViewContainerRef; public showNotification<T extends NotificationBase>(componentRef: ComponentRef<T>, timeOut: number) { const toast = componentRef; this.notificationBlock.insert(toast.hostView); let subscription = toast.instance.getClosedEvent() .subscribe(()=> { this.destroyComponent(toast, subscription); }); setTimeout(()=> { toast.instance.close(); }, timeOut); } private destroyComponent<T extends NotificationBase>(componentRef: ComponentRef<T>, subscription: Subscription) { componentRef.destroy(); subscription.unsubscribe(); } }
export abstract class NotificationBase { protected closedEvent = new Subject(); public getClosedEvent(){ return this.closedEvent; } public abstract close(): void; }
@Component({ selector : 'tiny-notification', template : ` <div class="container"> <div class="header-block"> <h3 class="header-title">{{header}}</h3> <a class="close-button" (click)="close()">x</a> </div> <div class="content"> <span>{{description}}</span> </div> </div>` }) export class TinyNotificationComponent extends NotificationBase { public header: string; public description: string; close() { this.closedEvent.next(); this.closedEvent.complete(); } }
@Injectable() export class NotificationManager { private notificationBlock: ViewContainerRef; private notificationPanel: NotificationPanelComponent; constructor(private componentFactoryResolver: ComponentFactoryResolver) { } public init(notificationBlock: ViewContainerRef) { this.notificationBlock = notificationBlock; const component = this.createComponent(NotificationPanelComponent); this.notificationPanel = component.instance; this.notificationBlock.insert(component.hostView); } ... public showToast(header: string, description: string, timeOut: number = 3000) { const component = this.createNotificationWithData<TinyNotificationComponent>(TinyNotificationComponent, { header : header, description: description }); this.notificationPanel.showNotification(component, timeOut); } ...
entryComponents : Array<Type<any>|any[]> Specifies a list of components that should be compiled when this module is defined. For each component listed here, Angular will create a ComponentFactory and store it in the ComponentFactoryResolver.
@NgModule({ declarations : [TinyNotificationComponent, NotificationPanelComponent, ModalDialogComponent], entryComponents: [TinyNotificationComponent, NotificationPanelComponent, ModalDialogComponent], providers : [NotificationManager] }) export class NotificationModule { }
... export class DynamicModalPageComponent implements OnInit { .... constructor(private notificationManager: NotificationManager) { } public ngOnInit(): void { this.notificationManager.init(this.notificationBlock); } public showToast(header: string, description: string) { this.notificationManager.showToast(header, description, 3000); } public showDialog(header: string, description: string) { this.notificationManager.showDialog(ModalDialogComponent, header, description) .subscribe((x: ModalDialogResult)=> { if (x == ModalDialogResult.Confirmed) { this.showToast(header, "modal dialog is confirmed"); } else { this.showToast(header, "modal dialog is closed"); } }); } }
Source: https://habr.com/ru/post/319386/
All Articles