Skip to content

Commit af69b74

Browse files
committed
make keyboard accessible modal
1 parent 591d02b commit af69b74

File tree

11 files changed

+147
-70
lines changed

11 files changed

+147
-70
lines changed

angular.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
],
3838
"optimization": true,
3939
"outputHashing": "all",
40-
"sourceMap": false,
40+
"sourceMap": true,
4141
"extractCss": true,
4242
"namedChunks": false,
4343
"aot": true,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@angular/platform-browser-dynamic": "^6.0.3",
2222
"@angular/router": "^6.0.3",
2323
"core-js": "^2.5.4",
24-
"rxjs": "^6.0.0",
24+
"rxjs": "6.0.0",
2525
"zone.js": "^0.8.26"
2626
},
2727
"devDependencies": {

src/app/app.component.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
html,body{
22
background: #CCCCCC;
3+
}
4+
h4{
5+
margin: 20px 0;
36
}

src/app/app.component.html

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<!--The content below is only a placeholder and can be replaced.-->
2-
<div class="container">
3-
<h1 align="center">
4-
Welcome to {{ title }}!
5-
</h1>
2+
<div class="container text-center">
3+
<h4 align="center">
4+
Dynamically Loaded Bootstrap Modal Component by Angular-6
5+
</h4>
66

7-
8-
<div class="col-md-2">
7+
<div class="row">
8+
<div class="col-md-2 col-md-offset-4">
99
<button type="button"
1010
class="btn btn-success btn-block"
1111
modal-box
1212
[title]='"Demo Modal 1"'
13-
[componentData]="data1"
13+
[componentData]="dataObj1"
1414
[componentName]="'Demo1Component'">
1515
Demo Modal 1
1616
</button>
@@ -21,12 +21,12 @@ <h1 align="center">
2121
class="btn btn-danger btn-block"
2222
modal-box
2323
[title]='"Demo Modal 2"'
24-
[componentData]="data2"
24+
[componentData]="dataObj2"
2525
[componentName]="'Demo2Component'">
2626
Demo Modal 2
2727
</button>
2828
</div>
29-
29+
</div>
3030

3131
</div>
3232

src/app/app.component.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
1-
import { Component } from '@angular/core';
1+
import { Component, OnInit } from '@angular/core';
22

33
@Component({
44
selector: 'app-root',
55
templateUrl: './app.component.html',
66
styleUrls: ['./app.component.css']
77
})
8-
export class AppComponent {
9-
data1 : string;
10-
data2 : string;
8+
export class AppComponent implements OnInit {
9+
dataObj1: any;
10+
dataObj2: any;
1111

1212

1313
ngOnInit() {
14-
this.data1="The content is displayed from Demo1 component";
15-
this.data2="The content is displayed from Demo2 component";
14+
this.dataObj1 = {
15+
heading: 'Modal Heading One',
16+
content: 'Lorem Ipsum is simply dummy text of the printing and <a href="#">typesetting industry</a>.'
17+
};
18+
this.dataObj2 = {
19+
heading: 'Modal Heading Two',
20+
content: 'Lorem Ipsum is simply dummy text of the printing and <a href="#">typesetting industry</a>.'
21+
};
22+
}
23+
24+
setFocusBtn(evt) {
25+
console.log(evt);
1626
}
1727

1828
}

src/app/demo1/demo1.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<h1>Demo1 Componenet Load</h1>
2-
<h4>{{data}}</h4>
1+
<h4>{{data.heading}}</h4>
2+
<p [innerHTML]="data.content"></p>

src/app/demo2/demo2.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<h1>Demo2 Componenet Load</h1>
2-
<h4>{{data}}</h4>
1+
<h4>{{data.heading}}</h4>
2+
<p [innerHTML]="data.content"></p>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
11
.modal{
22
display: block;
3+
z-index: 9999;
4+
}
5+
.modal-backdrop{
6+
z-index: 9999;
7+
}
8+
.modal-dialog:focus, .modal-header .close:focus {
9+
box-shadow: 0 0 0 2px #adf;
10+
outline: none;
11+
}
12+
.modal-header{
13+
background-color: #056cce;
14+
color: #FFF;
15+
}
16+
.modal-body{
17+
background-color: #fff;
18+
}
19+
.modal-header .close{
20+
color: #fff;
21+
opacity: 1;
22+
font-size: 30px;
23+
margin-top: -5px;
324
}
Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
<!-- Modal -->
2-
<div class="modal fade in" role="dialog" tabindex="-1" aria-hidden="true" #thisModal>
3-
<div class="modal-dialog">
4-
5-
<!-- Modal content-->
6-
<div class="modal-content">
7-
<div class="modal-header">
8-
<button type="button" class="close" (click)="closeModal()"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
9-
<h4 class="modal-title">{{ title }}</h4>
10-
</div>
11-
<div class="modal-body">
12-
<div #datacontainer></div>
13-
</div>
14-
<div class="modal-footer">
15-
<button type="button" class="btn btn-default" (click)="closeModal()">Close</button>
16-
</div>
2+
<div class="modal-box" #modalBox>
3+
<div class="modal" role="dialog" tabindex="-1" aria-hidden="true">
4+
<div class="modal-dialog" tabindex="0" role="dialog" [attr.aria-label]="title">
5+
<!-- Modal content-->
6+
<div class="modal-header">
7+
<button type="button" aria-label="Close" tabindex="0" class="close" (click)="closeModal()">
8+
<span aria-hidden="true">&times;</span>
9+
<span class="sr-only">Close</span>
10+
</button>
11+
<h4 class="modal-title">{{ title }}</h4>
12+
</div>
13+
<div class="modal-body" tabindex="-1">
14+
<div #datacontainer></div>
15+
</div>
1716
</div>
18-
1917
</div>

src/app/modal-dialog/modal-dialog.component.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
2-
Component, Input, OnInit,
3-
ComponentFactoryResolver, ViewContainerRef, ViewChild, ElementRef, Renderer2, AfterContentInit
2+
Component, Input, OnInit, ComponentFactoryResolver, ViewContainerRef, ViewChild,
3+
ElementRef, Renderer2, AfterContentInit, EventEmitter, Output, HostListener, OnChanges, AfterViewInit
44
} from '@angular/core';
55
import { ComponentLoaderService } from '../component-loader.service';
66

@@ -11,11 +11,13 @@ import { ComponentLoaderService } from '../component-loader.service';
1111
styleUrls: ['./modal-dialog.component.css']
1212
})
1313

14-
export class ModalDialogComponent implements OnInit, AfterContentInit {
14+
export class ModalDialogComponent implements OnInit, AfterContentInit, AfterViewInit {
1515
@Input() title: string;
1616
@Input() componentData: string;
1717
@Input() componentName: any;
18-
public name : any;
18+
@Output() close = new EventEmitter<any>();
19+
public name: any;
20+
public overlayDiv: any;
1921

2022
@ViewChild('datacontainer', { read: ViewContainerRef }) entry: ViewContainerRef;
2123

@@ -24,34 +26,59 @@ export class ModalDialogComponent implements OnInit, AfterContentInit {
2426
private ren: Renderer2,
2527
private viewContainer: ViewContainerRef,
2628
private resolver: ComponentFactoryResolver,
27-
private loaderService :ComponentLoaderService
29+
private loaderService: ComponentLoaderService
2830
) {}
2931

30-
public div = this.ren.createElement('div');
31-
32-
ngOnInit() {
33-
32+
public div = this.ren.createElement('div');
33+
34+
ngOnInit() {
35+
console.log('Modal Component is called.');
3436
}
35-
36-
ngAfterContentInit(){
37+
38+
ngAfterContentInit() {
3739
this.ren.addClass(this.el.nativeElement.ownerDocument.body, 'modal-open');
38-
this.ren.appendChild(this.el.nativeElement, this.div);
40+
this.ren.insertBefore(this.el.nativeElement.children[0], this.div, this.el.nativeElement.children[0].children[0]);
3941
this.ren.setAttribute(this.div , 'class', 'modal-backdrop fade in');
42+
this.ren.setAttribute(this.div , 'tabindex', '-1');
4043
this.createModalPopup();
44+
console.log(this.el.nativeElement.children[0]);
45+
}
46+
47+
ngAfterViewInit() {
48+
this.ren.listen(this.overlayDiv, 'click', (event) => {
49+
this.closeModal();
50+
});
51+
this.ren.listen(this.el.nativeElement, 'keydown', (event) => {
52+
console.log('event keydown => ', event);
53+
if (event.keyCode === 27 || event.key === 'Escape' || event.which === 27) { // ESCAPE key from keyboard
54+
this.closeModal();
55+
event.preventDefault();
56+
}
57+
});
4158
}
4259

43-
createModalPopup(){
60+
createModalPopup() {
61+
console.log('createModalPopup is called.');
4462
const name = this.loaderService.getComponent(this.componentName);
45-
console.log("Component Name => ",name);
63+
console.log('Component Name => ', name);
4664
const myFactory = this.resolver.resolveComponentFactory(<any>name);
4765
const myRef = this.entry.createComponent(myFactory);
4866
myRef.instance['data'] = this.componentData;
67+
this.overlayDiv = this.el.nativeElement.children[0];
68+
console.log('overlayDiv => ', this.el.nativeElement.children[0]);
69+
this.setFocus();
70+
}
71+
72+
setFocus() {
73+
const focusDiv = this.el.nativeElement.children[0].children[1].children[0];
74+
console.log('focusDiv => ', focusDiv);
75+
focusDiv.focus();
4976
}
5077

5178
closeModal() {
5279
this.ren.removeClass(this.el.nativeElement.ownerDocument.body, 'modal-open');
53-
this.ren.removeChild(this.el.nativeElement, this.div);
5480
this.el.nativeElement.remove();
81+
this.close.emit('close');
5582
}
5683

5784

0 commit comments

Comments
 (0)