[Angular] Implementing A General Communication Mechanism For Directive Interaction
We have modal implement and now we want to implement close functionality.
Becuase we use a structure directive to do open modal on click functionality. So as you can see, the directive and modal component is spreated. We need to have some way to communcation between directive and component.
<ng-template [auModalOpenOnClick]="[loginButton, signUpButton]"> <au-modal class="auth-modal" [body]="newModelBody"> <!-- modal body --> </au-modal> </ng-template>
One general communction mechanism is though serivce.
So we need to create a service which only for AuModal component:
import {NgModule, ModuleWithProviders} from '@angular/core';
import {AuModalComponent} from './au-modal.component';
import {CommonModule} from '@angular/common';
import { AuModalOpenOnClickDirective } from './au-modal-open-on-click.directive';
import {AuModalService} from 'app/au-modal/au-modal.service';
@NgModule({
declarations: [AuModalComponent, AuModalOpenOnClickDirective],
imports: [
CommonModule
],
exports: [
AuModalComponent,
AuModalOpenOnClickDirective
]
})
export class AuModalModule {
/*
* Inject AuModuleService here instead of global providers is for lazy loading
* to prevent duplicate serivce name.
* */
static forRoot(): ModuleWithProviders {
return {
ngModule: AuModalModule,
providers: [
AuModalService
]
}
}
}
Because we don't want it to be global, it might affect lazy loading, have naming conflicts with other third party libs. Therefore we use 'forRoot' static method on the AuModalModule. This approach is good for lazy loading.
Service:
Service is Observable based implementation. It mean we gonna have one public method call 'close' and an public observable variable call 'close$'.
Once close() method was called, 'close$' can get notifiied.
import { Injectable } from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; @Injectable() export class AuModalService { private subject = new Subject(); close$: Observable<any> = this.subject.asObservable(); constructor() { } close() { this.subject.next(); } }
AuModal component: We want user click outside modal body to close the modal, so we bind 'closeModal' function tot he overlay wrapper. And also we don't want user click modal body itself to trigger the close event also. So we have second method called 'cancelCloseModal' to stop event propagation.
<div class="modal-overlay" (click)="closeModal()"> <div class="modal-body" (click)="cancelCloseModal($event)"> <ng-container *ngIf="body; else projectionBody"> <ng-container *ngTemplateOutlet="body"></ng-container> </ng-container> <ng-template #projectionBody> <ng-content></ng-content> </ng-template> </div> </div>
closeModal() { this.auModelService.close(); } cancelCloseModal(evt: KeyboardEvent) { evt.preventDefault(); evt.stopPropagation(); }
Now the only thing leave to do is subscribe the 'close$' observable inside the directive, once event was triggered, we clear the component.
Directive:
import {Directive, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core'; import {AuModalService} from './au-modal.service'; @Directive({ selector: '[auModalOpenOnClick]' }) export class AuModalOpenOnClickDirective implements OnInit{ ngOnInit(): void { this.auModalService.close$ .subscribe(() => this.viewContainer.clear()); } @Input() set auModalOpenOnClick (els) { let elements: HTMLBaseElement[]; if(Array.isArray(els)) { elements = els; } else { elements = [els]; } elements.forEach(el => { el.addEventListener('click', () => { this.viewContainer.clear(); this.viewContainer.createEmbeddedView(this.template); }); }); } constructor( private template: TemplateRef<any>, private viewContainer: ViewContainerRef, private auModalService: AuModalService ) { } }
相关文章
- [Angular 8] Take away: Tools for Fast Angular Applications
- [Angular] Create a custom validator for template driven forms in Angular
- [Angular] Http Custom Headers and RequestOptions
- [Angular] USING ZONES IN ANGULAR FOR BETTER PERFORMANCE
- [Angular Directive] Structure directive and <template>
- [Angular2 Form] Create Radio Buttons for Angular 2 Forms
- [Angular 2] @Input & @Output Event with ref
- [AngularJS] angular-md-table for Angular material design
- [Angular 2] Using ng-model for two-way binding
- [Angular 2] Using ng-for to repeat template elements
- [Angular] Standalone component - routes top level provide share for all child routes
- [Unit Testing Angular] RxJS Marble testing for Component
- [Angular] Create a custom validator for template driven forms in Angular
- [Angular] Test Container component with async provider
- [Angular Directive] Structure directive and <template>
- [Angular2 Animation] Use Keyframes for Fine-Tuned Angular 2 Animations
- [Angular 2] BYPASSING PROVIDERS IN ANGULAR 2
- 关于 Angular 12 的 inlineCriticalCss 选项
- Angular InjectionToken APP_INITIALIZER multi 标志位不同值的情况讨论
- 关于 Angular 项目里的 index.ts
- 如何以可视化方式显示 Angular 应用构建后的 bundle 文件大小
- Angular Component input字段传递值的几种变式 variant
- Angular应用的angular.json文件字段一览
- angular input 为file on-change 无效