Angular - Material - Popup Forms

Angular Material popup forms

I like the angular material popup window more than the bootstrap method. I do not know if it’s because I learned bootstrap first, or if the material form is a bit more elegant. I suspect the latter.

Material includes a dialog control to support popups.

The popup window Component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<h1 mat-dialog-title>{{title}}</h1>
<div mat-dialog-content>
<form [formGroup]="policyAttachmentForm" >
<div fxLayout="row wrap" fxLayoutGap="8px grid" >
<div fxFlex="100%">
<mat-form-field class="full-width">
<input
matInput
type='number'
#txtPolicyId
autofocus="true"
placeholder="Policy Id"
formControlName="policyId"
required
>
<mat-error *ngIf="errorFormData?.PolicyId">
{{errorFormData?.PolicyId}}
</mat-error>
</mat-form-field>

</div>
<div fxFlex="100%">
<mat-form-field class="full-width">
<mat-select
placeholder="Upload Location CodesId"
formControlName="uploadLocationCodesId"
required
>
<mat-option
*ngFor="let uploadLocationCode of UploadLocationCodes"
value="{{uploadLocationCode.id}}">
{{uploadLocationCode.description}}
</mat-option>
</mat-select>
<mat-error *ngIf="errorFormData?.UploadLocationCodesId">
{{errorFormData?.UploadLocationCodesId}}
</mat-error>
</mat-form-field>

</div>
<!—
There are more controls, but they are just that (more controls
Not needed to better understand the popup
-->

</div>

<!—
There’s a section called mat-dialog-actions that is used to hold
The save buttons
-->

<div mat-dialog-actions>
<button
class="submit-btn"
color="primary"
mat-raised-button
type="submit"
(click)="savePolicyAttachment()"
>Submit
</button>

<button
class="submit-btn"
mat-raised-button
type="submit"
(click)="closeForm('canceled')"
>Cancel
</button>

</div>
</form>

</div>

As you can see, there is minimal coding needed for a popup form.

Here is the popup window Component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
import { PolicyAttachmentService } from '../policy-attachment.service';
import { FormBuilder, FormControl, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { PolicyAttachment } from '../policy-attachment.model';
import { CommonService } from 'src/app/common.service';
import { Component, OnInit, ViewChild, ElementRef} from '@angular/core';
import { FormGroup } from '@angular/forms';
import Swal from 'sweetalert2/dist/sweetalert2.js';
import { UploadLocationCodeService } from '../../upload-location-code/upload-location-code.service';
import { UploadLocationCode } from '../../upload-location-code/upload-location-code.model';
import { Inject } from '@angular/core';
//
// these are the controls used for a popup window
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';

@Component({
selector: 'policy-attachment-details',
templateUrl: './policy-attachment-details.component.html',
styleUrls: ['./policy-attachment-details.component.css']
})
export class PolicyAttachmentDetailsComponent implements OnInit {
public UploadLocationCodes: UploadLocationCode[];
id: number
public title: string; // 20200916 - ade public
errorFormData: any; // API will throw errors into this structure
//
// hmm, I cannot remember why I added this
//
@ViewChild("txtPolicyId") txtPolicyId: ElementRef;

public serviceErrorData: any;

policyAttachmentForm = this.formBuilder.group ( {
id: ['', [
Validators.required,
]],
policyId: ['', [
Validators.required,
]],
uploadLocationCodesId: ['', [
Validators.required,
]],
fileNameOriginal: ['', [
Validators.required,
Validators.maxLength(200),
]],
fileNameUploadedAs: ['', [
Validators.required,
Validators.maxLength(200),
]],
uploadFileTypeCodesId: ['', [
]],
})

constructor(
private service: PolicyAttachmentService,
private formBuilder: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private commonService: CommonService,
private UploadLocationCodesService: UploadLocationCodeService,
//
// these next two were added to support the popup logic
//
public dialogRef: MatDialogRef<PolicyAttachmentDetailsComponent>,
// data is a variable containing data being passed up from the main form.
@Inject(MAT_DIALOG_DATA) public data: any
) {
}

ngOnInit() {
this.getAllUploadLocationCodes();

if (this.isAddForm()) {
this.title="Add a new Policy Attachment";
} else {
this.title="Modify an existing Policy Attachment";
this.getPolicyAttachment();
}
}

getAllUploadLocationCodes() {
this.UploadLocationCodesService.getAllUploadLocationCodes()
.subscribe(result => {
this.UploadLocationCodes = result.model;
})
}


ngAfterViewInit() {
this.txtPolicyId.nativeElement.focus();
}

public isAddForm() {
console.log ("isAddForm - " + (this.data.PolicyAttachment == null ? "true" : "false"));
//return this.id === 0;
return this.data.PolicyAttachment == null;
}

public getPolicyAttachment() {
console.log("getPolicyAttachment");
let aPolicyAttachment = this.data.PolicyAttachment;
console.log ("getPolicyAttachment - " + aPolicyAttachment.uploadLocationCodesId);

this.policyAttachmentForm.setValue ( {
id: aPolicyAttachment.id,
policyId: aPolicyAttachment.policyId,
uploadLocationCodesId: String(aPolicyAttachment.uploadLocationCodesId),
fileNameOriginal: aPolicyAttachment.fileNameOriginal,
fileNameUploadedAs: aPolicyAttachment.fileNameUploadedAs,
uploadFileTypeCodesId: String(aPolicyAttachment.uploadFileTypeCodesId),
});

this.policyAttachmentForm.markAsPristine();
this.policyAttachmentForm.updateValueAndValidity();

}

public savePolicyAttachment() {
console.log("savePolicyAttachment");
if (this.isAddForm()) {

}
let policyAttachment: PolicyAttachment = {
id: this.isAddForm() ? null : this.policyAttachmentForm.controls.id.value,
policyId: this.policyAttachmentForm.controls.policyId.value,
uploadLocationCodesId: Number(this.policyAttachmentForm.controls.uploadLocationCodesId.value),
fileNameOriginal: this.policyAttachmentForm.controls.fileNameOriginal.value,
fileNameUploadedAs: this.policyAttachmentForm.controls.fileNameUploadedAs.value,
uploadFileTypeCodesId: Number(this.policyAttachmentForm.controls.uploadFileTypeCodesId.value),
}

if (this.isAddForm()) {
// add
this.errorFormData = null;
console.log("Add");
this.service.createNewPolicyAttachment (policyAttachment )
.subscribe(result => { console.log("Add - Succeeded");
if (result.wasSuccessful === true) {
this.commonService.DisplayToastrMessage("The Policy Type Code has been added into the system.");
//
// had this been a normal form, I would now navigate to the list
// page.
//this.router.navigate(['/policyAttachments']);
//
// but as it’s a popup, I need only close the form.
// ‘I’m going to pass a value of ‘added’ back. I’m not sure
// why, maybe it will come in handy in the future.
//
this.closeForm('added');
} else {
this.commonService.DisplayErrorWithOk("Error adding Policy Attachment", result.message.friendlyMessage);
}
},
apiError => {
console.log(`Add - Error. - ` + apiError.error.errors);
if (apiError.error && apiError.error.errors) {
console.log(`Add - Errors - ${apiError.error.errors}`);
this.errorFormData=apiError.error.errors;
};
});
} else {
// edit
console.log("Edit");
this.service.updatePolicyAttachment(policyAttachment)
.subscribe(result => {
if (result.wasSuccessful === true) {
this.commonService.DisplayToastrMessage("Policy Attachment has been Updated");
//
// had this been a normal form, I would now navigate to the list
// page.

//this.employeeForm.markAsPristine();
//this.router.navigate(['/policyAttachments']);
//
// but as it’s a popup, I need only close the form.
// ‘I’m going to pass a value of ‘savedback. I’m not sure
// why, maybe it will come in handy in the future.
//
this.closeForm('saved');
} else {
this.commonService.DisplayErrorWithOk("Error updating Policy Attachment", result.message.friendlyMessage);
};
},
apiError => {
if (apiError.error && apiError.error.errors) {
this.errorFormData=apiError.error.error;
};
}
);
}
}


//
// the close form is used to close the dialog window
//
closeForm(status: string): void {
this.dialogRef.close({
//
// this object structure will be passed to the main form
// right now a simple status parameter with values like
// added, saved, etc.
//
status: status
});
}

}

Here is the main form, that has a function to call a popup

Here is the main form.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { Component, OnInit } from '@angular/core';
import { PolicyAttachmentService } from '../policy-attachment.service';
import { ActivatedRoute } from '@angular/router';
import { PolicyAttachment } from '../policy-attachment.model';
import { Router } from '@angular/router';
//
// Mat Dialog is used
//
import { MatDialog } from '@angular/material/dialog';
//
// I need a reference to the popup component
import { PolicyAttachmentDetailsComponent} from '../policy-attachment-details/policy-attachment-details.component';

@Component({
selector: 'policy-attachment-list',
templateUrl: './policy-attachment-list.component.html',
styleUrls: ['./policy-attachment-list.component.css']
})
export class PolicyAttachmentListComponent implements OnInit {
public policyAttachments: PolicyAttachment[];
displayedColumns: string[] = [
'policyId',
'uploadLocationCodesId',
'fileNameOriginal',
'fileNameUploadedAs',
'uploadFileTypeCodesId',
];
constructor(
private service: PolicyAttachmentService,
private route: ActivatedRoute,
private router: Router,
public dialog: MatDialog,
) {
this.getPolicyAttachments();
}

ngOnInit() {
}

public getPolicyAttachments() {
this.service.getAllPolicyAttachments()
.subscribe(
result => {
console.log ("getAllPolicyAttachments succeeded");
console.log (result);
if (result.wasSuccessful === false) {
alert("Error trying to retrieve PolicyAttachments");
} else {
this.policyAttachments = result.model;
}
},
error => {
alert("Error trying to retrieve Policy Attachments!");
}
);
}

// here we wil edit
//
// when a row is clicked this function is called. I also call this
// function with the Add new button. Row click passes a policyAttachment
// object. New passes a null
public onRowClicked(policyAttachment: object | null) {

//
// normally I would switch to the add/edit details page. But as this
// is a popup, there is no need for router
//this.router.navigate(['/policyAttachment/', policyAttachment.id]);

//
// first create a dialog box object. Pass in the component, and an
// object containing specs like width and the data you want to pass
// to the popup. In my case the policyattachment object that was
// passed to this method
//
const dialogRef = this.dialog.open(PolicyAttachmentDetailsComponent, {
width: '400px',
data: { PolicyAttachment: policyAttachment }
});

//
// second thing you want to do is setup an event which will be called
// when the popup is closed. I suppose there is a results object passed
// to it which currenty just has a status. It’s not used in my logic
// I just freshen my list control
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed', result);
this.getPolicyAttachments();
});

}


}

The controlling module had a few changes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { PolicyAttachmentListComponent } from './policy-attachment-list/policy-attachment-list.component';
import { PolicyAttachmentDetailsComponent } from './policy-attachment-details/policy-attachment-details.component';
import { AngularMaterialModule } from '../material.module';
import { FlexLayoutModule } from '@angular/flex-layout';
//
// this was required
//
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';

@NgModule({
declarations: [
PolicyAttachmentListComponent,
PolicyAttachmentDetailsComponent,
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule,
AngularMaterialModule,
FlexLayoutModule,
// added
MatDialogModule,
],
exports: [
ReactiveFormsModule,
],
entryComponents: [
PolicyAttachmentListComponent,
],
// I also had to add providers
providers: [
{ provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: [] },
]
})

export class PolicyAttachmentModule { }

References