|
1 | 1 | # Dynamically-Loaded-Bootstrap-Modal-Component-by-Angular-6 |
2 | 2 | Dynamically Loaded Bootstrap Modal Component by Angular 6 |
| 3 | + |
| 4 | +Create the application using the Angular CLI command. |
| 5 | + |
| 6 | + ng new modal |
| 7 | +Update the index.html file to reference the Bootstrap Css - using the CDN. |
| 8 | + |
| 9 | + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> |
| 10 | + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> |
| 11 | + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> |
| 12 | + |
| 13 | +App Component |
| 14 | +Keep two buttons in app.componenet.html for loading two different modal popups. |
| 15 | + |
| 16 | +app.component.html |
| 17 | + |
| 18 | + <div class="col-md-2"> |
| 19 | + <button type="button" |
| 20 | + class="btn btn-success btn-block" |
| 21 | + modal-box |
| 22 | + [title]='"Demo Modal 1"' |
| 23 | + [componentData]="data1" |
| 24 | + [componentName]="'Demo1Component'"> |
| 25 | + Demo Modal 1 |
| 26 | + </button> |
| 27 | + </div> |
| 28 | + |
| 29 | + <div class="col-md-2"> |
| 30 | + <button type="button" |
| 31 | + class="btn btn-danger btn-block" |
| 32 | + modal-box |
| 33 | + [title]='"Demo Modal 2"' |
| 34 | + [componentData]="data2" |
| 35 | + [componentName]="'Demo2Component'"> |
| 36 | + Demo Modal 2 |
| 37 | + </button> |
| 38 | + </div> |
| 39 | + app.component.ts |
| 40 | + |
| 41 | + export class AppComponent { |
| 42 | + data1 : string; |
| 43 | + data2 : string; |
| 44 | + |
| 45 | + |
| 46 | + ngOnInit() { |
| 47 | + this.data1="The content is displayed from Demo1 component"; |
| 48 | + this.data2="The content is displayed from Demo2 component"; |
| 49 | + } |
| 50 | + |
| 51 | + } |
| 52 | +app.module.ts |
| 53 | + |
| 54 | + import { BrowserModule } from '@angular/platform-browser'; |
| 55 | + import { NgModule } from '@angular/core'; |
| 56 | + import { HttpClientModule } from '@angular/common/http'; |
| 57 | + |
| 58 | + import { AppComponent } from './app.component'; |
| 59 | + import { Demo1Component } from './demo1/demo1.component'; |
| 60 | + import { Demo2Component } from './demo2/demo2.component'; |
| 61 | + import { ModalDialogComponent } from './modal-dialog/modal-dialog.component'; |
| 62 | + import { ModalDirective } from './modal.directive'; |
| 63 | + import { DatacontainerDirective } from './modal-dialog/datacontainer.directive'; |
| 64 | + import { ComponentLoaderService } from './component-loader.service'; |
| 65 | + |
| 66 | + @NgModule({ |
| 67 | + declarations: [ |
| 68 | + AppComponent, |
| 69 | + Demo1Component, |
| 70 | + Demo2Component, |
| 71 | + ModalDialogComponent, |
| 72 | + ModalDirective, |
| 73 | + DatacontainerDirective |
| 74 | + ], |
| 75 | + providers: [ComponentLoaderService], |
| 76 | + imports: [ |
| 77 | + BrowserModule, |
| 78 | + HttpClientModule |
| 79 | + ], |
| 80 | + entryComponents: [ |
| 81 | + Demo1Component, |
| 82 | + Demo2Component, |
| 83 | + ModalDialogComponent |
| 84 | + ], |
| 85 | + bootstrap: [AppComponent] |
| 86 | + }) |
| 87 | + export class AppModule { } |
| 88 | + |
| 89 | +entryComponent is used for components declaration which will be loaded dynamically. |
| 90 | + |
| 91 | +Modal Directive |
| 92 | +modal.directive.ts |
| 93 | + |
| 94 | + import { Directive, Input, Output, EventEmitter, ElementRef, HostListener, Renderer2, ViewContainerRef, ComponentFactoryResolver } from '@angular/core'; |
| 95 | + import { ModalDialogComponent } from './modal-dialog/modal-dialog.component'; |
| 96 | + |
| 97 | + @Directive({ |
| 98 | + selector: '[modal-box]' |
| 99 | + }) |
| 100 | + export class ModalDirective { |
| 101 | + @Input() title: string; |
| 102 | + @Input() componentData: string; |
| 103 | + @Input() componentName: string; |
| 104 | + |
| 105 | + |
| 106 | + @HostListener('click', ['$event']) |
| 107 | + |
| 108 | + /* modal create */ |
| 109 | + openModal() { |
| 110 | + this.createModalDialog(ModalDialogComponent); |
| 111 | + } |
| 112 | + |
| 113 | + constructor( |
| 114 | + private el: ElementRef, |
| 115 | + private ren: Renderer2, |
| 116 | + private viewContainer: ViewContainerRef, |
| 117 | + private componentFactoryResolver: ComponentFactoryResolver |
| 118 | + ) { } |
| 119 | + |
| 120 | + ngOnInit() { |
| 121 | + } |
| 122 | + |
| 123 | + createModalDialog(modalDialogComponent) { |
| 124 | + this.viewContainer.clear(); |
| 125 | + const modalDialogComponentFactory = this.componentFactoryResolver.resolveComponentFactory(modalDialogComponent); |
| 126 | + const modalDialogComponentRef = this.viewContainer.createComponent(modalDialogComponentFactory); |
| 127 | + modalDialogComponentRef.instance['title'] = this.title; |
| 128 | + modalDialogComponentRef.instance['componentData'] = this.componentData; |
| 129 | + modalDialogComponentRef.instance['componentName'] = this.componentName; |
| 130 | + |
| 131 | + return modalDialogComponentRef; |
| 132 | + } |
| 133 | + |
| 134 | + |
| 135 | + } |
| 136 | +createModalDialog() function is used to create modal dialog componenet dynamically. You have to send modalDialogComponent through resolveComponenetFactory to create factory then will create component using createComponent function. We are fetching values of title, componentData, componentName from buttons which were declared in app.componenet.html. These value sends using modalDialogComponentRef.instance to modal-dialog.componenet.ts. |
| 137 | + |
| 138 | +Modal Dialog Component |
| 139 | +modal-dialog.component.css |
| 140 | + |
| 141 | + .modal{ |
| 142 | + display: block; |
| 143 | + } |
| 144 | +modal-dialog.component.html |
| 145 | + |
| 146 | + <!-- Modal --> |
| 147 | + <div class="modal fade in" role="dialog" tabindex="-1" aria-hidden="true" #thisModal> |
| 148 | + <div class="modal-dialog"> |
| 149 | + |
| 150 | + <!-- Modal content--> |
| 151 | + <div class="modal-content"> |
| 152 | + <div class="modal-header"> |
| 153 | + <button type="button" class="close" (click)="closeModal()"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> |
| 154 | + <h4 class="modal-title">{{ title }}</h4> |
| 155 | + </div> |
| 156 | + <div class="modal-body"> |
| 157 | + <div #datacontainer></div> |
| 158 | + </div> |
| 159 | + <div class="modal-footer"> |
| 160 | + <button type="button" class="btn btn-default" (click)="closeModal()">Close</button> |
| 161 | + </div> |
| 162 | + </div> |
| 163 | + |
| 164 | + </div> |
| 165 | + |
| 166 | +modal-dialog.component.ts |
| 167 | + |
| 168 | + import { |
| 169 | + Component, Input, OnInit, |
| 170 | + ComponentFactoryResolver, ViewContainerRef, ViewChild, ElementRef, Renderer2, AfterContentInit |
| 171 | + } from '@angular/core'; |
| 172 | + import { ComponentLoaderService } from '../component-loader.service'; |
| 173 | + |
| 174 | + |
| 175 | + @Component({ |
| 176 | + selector: 'modal-dialog', |
| 177 | + templateUrl: './modal-dialog.component.html', |
| 178 | + styleUrls: ['./modal-dialog.component.css'] |
| 179 | + }) |
| 180 | + |
| 181 | + export class ModalDialogComponent implements OnInit, AfterContentInit { |
| 182 | + @Input() title: string; |
| 183 | + @Input() componentData: string; |
| 184 | + @Input() componentName: any; |
| 185 | + public name : any; |
| 186 | + |
| 187 | + @ViewChild('datacontainer', { read: ViewContainerRef }) entry: ViewContainerRef; |
| 188 | + |
| 189 | + constructor( |
| 190 | + private el: ElementRef, |
| 191 | + private ren: Renderer2, |
| 192 | + private viewContainer: ViewContainerRef, |
| 193 | + private resolver: ComponentFactoryResolver, |
| 194 | + private loaderService :ComponentLoaderService |
| 195 | + ) {} |
| 196 | + |
| 197 | + public div = this.ren.createElement('div'); |
| 198 | + |
| 199 | + ngOnInit() { |
| 200 | + |
| 201 | + } |
| 202 | + |
| 203 | + ngAfterContentInit(){ |
| 204 | + this.ren.addClass(this.el.nativeElement.ownerDocument.body, 'modal-open'); |
| 205 | + this.ren.appendChild(this.el.nativeElement, this.div); |
| 206 | + this.ren.setAttribute(this.div , 'class', 'modal-backdrop fade in'); |
| 207 | + this.createModalPopup(); |
| 208 | + } |
| 209 | + |
| 210 | + createModalPopup(){ |
| 211 | + const name = this.loaderService.getComponent(this.componentName); |
| 212 | + console.log("Component Name => ",name); |
| 213 | + const myFactory = this.resolver.resolveComponentFactory(<any>name); |
| 214 | + const myRef = this.entry.createComponent(myFactory); |
| 215 | + myRef.instance['data'] = this.componentData; |
| 216 | + } |
| 217 | + |
| 218 | + closeModal() { |
| 219 | + this.ren.removeClass(this.el.nativeElement.ownerDocument.body, 'modal-open'); |
| 220 | + this.ren.removeChild(this.el.nativeElement, this.div); |
| 221 | + this.el.nativeElement.remove(); |
| 222 | + } |
| 223 | + |
| 224 | + |
| 225 | + } |
| 226 | + |
| 227 | +this.ren.addClass is used to add 'modal-open' class on creating modal-dialog. this.ren.appendChild is used append div in body. this.ren.setAttribute is used to add class name 'modal-backdrop fade in' in div which shows when modal-dialog opens for overlay. |
| 228 | + |
| 229 | +this.createModalPopup() is called for loading component dynamically. Component is coming from buttons of app.componenet.html page and this.componentName is used to hold componenent name. loaderService is used to pass a component according to the component name. That component is passing to the resolveComponentFactory, createComponent function to create the component accordingly. We are passing the componentData using myRef.instance['data'] to component like demo1 or demo2. |
| 230 | + |
| 231 | +That data is fetching from demo1 and demo2 component using @Input() and displaing it from html file. |
| 232 | + |
| 233 | +closeModal() is used to remove the modal. this.el.nativeElement.remove() is used to remove DOM element. |
0 commit comments