-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathangularCRUD.txt
251 lines (239 loc) · 14.1 KB
/
angularCRUD.txt
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
INTRO
There are 2 ways to create forms in Angular
1. Template Driven Forms - are generally used to create simple forms
2. Model Driven Forms (Commonly called Reactive Forms) - are used to create complex forms. For example, if you want to add form controls dynamically or perform cross-field validation we use the Reactive forms approach
use ngx-bootstrap for datepicker and other controls. Refer third parties library in UI components section on https://angular.io/resources
Validation
By default Angular 4 and later versions disable browser native validation. To enable browser validation, include ngNativeValidate directive on the form tag
For Angular 2, you will have to explicitly disable browser validation by using novalidate attribute on the form tag
value vs ngValue -
<option [ngValue]="null">Select Department</option> - are using ngValue instead of value. If you use value, null is treated as a string and not as a null.
<option *ngFor="let dept of departments" [value]="dept.id">{{dept.name}}</option> - are using value instead of ngValue, because we just want the selected id as a string
Use ngValue when need to work with null or object and use value when need to work with string
The change event is fired only after the form control has lost focus. The input event is fired as the user changes the value
A form's control is touched when the control looses focus
Safe Navigation Operator(?) -
It is generally used when we are not sure if a property exists or not.
It safely handles null and undefined values, and very useful to prevent null-reference exceptions.
'email.errors?.required' is equivalent to 'email.errors && email.errors.required'
READ OPERATION
create models/employee.model.ts file and make a export class having properties for objects which we are going to read
TEMPLATE DRIVEN FORMS
<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employeeForm)">......</form>
#employeeForm is called the template reference variable. employeeForm variable holds a reference to the ngForm ie whole form.
Angular automatically attaches the ngForm directive to form tag.
The ngForm directive holds all the form controls that we create with ngModel directive and name attribute, and monitors their properties like value, dirty, touched, valid etc.
The form also has all these properties ie dirty, touched, valid etc
ngSubmit directive submits the form when we hit the enter key or when we click the Submit button
For control to be monitored by ngForm, name attribute is needed, as ngForm creates a property with same name as that of the control's.
By default, disabled form controls are not included in the Angular auto generated form model.
Initially, the value of the controls are set to undefined, if not set/initialized in the corresponding .ts file
MODEL DRIVEN FORMS
In the view template, bind the ngModel directive of an input field to it's corresponding property on the defined object in .ts file
snapshot of form code -
<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employee)">
<div class="form-group" [class.has-error]="name.invalid && name.touched">
<label for="name" class="control-label">Name</label>
<input id="name" required type="text" class="form-control" name="name" [(ngModel)]="employee.name" #name="ngModel"> //employee is the defined object in .ts file
<span class="help-block" *ngIf="name.invalid && name.touched">Name is required</span>
</div>
.
.
.
</form>
#employeeForm and #name template variable can be used to check properties like dirty, pristine, invalid, valid, untouched, touched
VALIDATOR
email: boolean | string
<input type="email" name="email" ngModel email>
<input type="email" name="email" ngModel [email]="true"> //Bind email directive to a boolean expression
example -
<div class="form-group" [class.has-error]="email.invalid && email.touched">
<label for="email" class="control-label">Email</label>
<input id="email" required email type="text" class="form-control" name="email" [(ngModel)]="employee.email" #email="ngModel">
<span class="help-block" *ngIf="email.errors?.required && email.touched">Email is required</span>
<span class="help-block" *ngIf="email.errors?.email && email.touched">Email is Invalid</span>
</div>
If the email is invalid, angular attaches email key to the errors collection. And when the email field is valid, the key email will not be in the errors collection.
required: boolean | string
pattern: string | RegExp
minlength: string
maxlength: string
CUSTOM VALIDATOR
To use a custom validator in a forms, we have to create the validator as a directive
directive.ts file
import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
import { Directive } from '@angular/core';
@Directive({
selector: '[appSelectValidator]',
providers: [{
provide: NG_VALIDATORS, //NG_VALIDATORS is a collection of validators. It already contains all the built-in validators like required, pattern
useExisting: SelectRequiredValidatorDirective, //adding 'SelectRequiredValidatorDirective' to NG_VALIDATORS token
multi: true //To specify that we want to add our validator to the list of validators, we set multi property to true
}]
})
export class SelectRequiredValidatorDirective implements Validator {
// Create input property to receive the specified default option value
@Input('appSelectValidator') //using alias name. 'appSelectValidator' to be used outside of the directive and 'defaultValue' to be used inside
defaultValue: string;
validate(control: AbstractControl): { [key: string]: any } | null { //AbstractControl extends both FormControl and FormGroup.
//returns an object if the validation fails or null if the validation succeeds
return control.value === defaultValue ? { 'defaultSelected': true } : null;
}
}
template.html
//since validator and input anme are the same, 'appSelectValidator="-1"' is equivalent to 'appSelectValidator [appSelectValidator]="-1"'
<select id="department" #department="ngModel" name="department" [(ngModel)]="employee.department" appSelectValidator="-1" class="form-control">
<option value="-1">Select Department</option>
<option *ngFor="let dept of departments" [value]="dept.id">{{dept.name}}</option>
</select>
PASSWORD AND CONFIRM PASSWORD VALIDATOR SCENARIO
Snippet of validator.ts file
export class ConfirmEqualValidatorDirective implements Validator {
@Input()
appConfirmEqualValidator: string;
validate(control: AbstractControl): { [key: string]: any } | null {
const controlToCompare = control.parent.get(this.appConfirmEqualValidator);
if (controlToCompare && controlToCompare.value !== control.value) {
return { 'notEqual': true };
}
return null;
}
}
Snippet of form.html file
<div class="form-group" [class.has-error]="confirmPassword.touched && confirmPassword.invalid">
<label for="confirmPassword" class="control-label">Confirm Password</label>
<input name="confirmPassword" appConfirmEqualValidator="password" required id="confirmPassword" type="text" class="form-control" [(ngModel)]="employee.confirmPassword" #confirmPassword="ngModel">
<span class="help-block" *ngIf="confirmPassword.touched && confirmPassword.errors?.required">Confirm Password is required</span>
<span class="help-block" *ngIf="confirmPassword.touched && confirmPassword.errors?.notEqual && !confirmPassword.errors?.required"> Password and Confirm Password does not match</span>
</div>
If you first change PASSWORD field and then the CONFIRM PASSWORD field, the validation works as expected.
Now if you go back and change the PASSWORD field, the validation will not be triggered. 2 ways to fix it -
updateValueAndValidity() -
When this method is called on a control, that control's validation logic is executed again
<input name="password" (input)="confirmPassword.control.updateValueAndValidity()" …>
ngModelGroup -
Use to create a sub-group within a form
Useful to validate a sub-group of elements on the form
Useful to group properties of the form model in to a nested object. The name of the ngModelGroup will become the key for the nested object in the form model
The ngModelGroup directive can only be used as a child of NgForm directive
Snippet of validator.ts file
<div ngModelGroup="passwordGroup" #passwordGroup="ngModelGroup" appConfirmEqualValidator>
Snippet of form.html file
export class ConfirmEqualValidatorDirective implements Validator {
validate(passwordGroup: AbstractControl): { [key: string]: any } | null {
const passwordField = passwordGroup.get('password');
const confirmPasswordField = passwordGroup.get('confirmPassword');
if (passwordField && confirmPasswordField && passwordField.value !== confirmPasswordField.value) {
return { 'notEqual': true };
}
return null;
}
}
RESET FORM
Clears all the form controls
Optionally the form controls can be initialized with default values
Resets the form state and individual controls state like dirty, pristine, valid, touched etc.
To RESET the form in HTML
<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employeeForm); employeeForm.reset()">
There are 2 ways to RESET the form in code
pass the form (NgForm) to the Submit() method and then call reset() method.
saveEmployee(empForm: NgForm): void {
const newEmployee: Employee = Object.assign({}, this.employee);
this._employeeService.save(newEmployee);
//calling save method on 'this.employee' would lead to abnormal behavior as reset will be executed first and then save service will be be called
empForm.reset();
this._router.navigate(['list']);
}
use the @ViewChild decorator to access form in code and then call reset() method.
@ViewChild('employeeForm') public createEmployeeForm: NgForm;
saveEmployee(): void {
const newEmployee: Employee = Object.assign({}, this.employee);
this._employeeService.save(newEmployee);
//calling save method on 'this.employee' would lead to abnormal behavior as reset will be executed first and then save service will be be called
this.createEmployeeForm.reset();
this._router.navigate(['list']);
}
UPDATE OPERATION
Below Route to support both creating a new employee and editing an existing employee.
If the id route parameter value is 0, then the form will be used to create a new employee. If the id value is not 0, then the form will be used to edit an existing employee.
{
path: 'edit/:id',
component: CreateEmployeeComponent,
canDeactivate: [CreateEmployeeCanDeactivateGuardService]
}
Changes in employee.service.ts : Change save() method as shown below
save(employee: Employee) {
if (employee.id === null) {
// reduce() method reduces the array to a single value. This method executes the provided function for each element of the array (from left-to-right)
// When we implement the server side service to save data to the database table, we do not have to compute the id, as the server will assing it
const maxId = this.listEmployees.reduce(function (e1, e2) { return (e1.id > e2.id) ? e1 : e2; }).id;
employee.id = maxId + 1;
this.listEmployees.push(employee);
} else {
const foundIndex = this.listEmployees.findIndex(e => e.id === employee.id);
this.listEmployees[foundIndex] = employee;
}
}
DELETE OPERATION
deleteEmployee(id: number) {
const i = this.listEmployees.findIndex(e => e.id === id);
if (i !== -1) {
this.listEmployees.splice(i, 1);
}
}
ANGULAR HTTPCLIENT POST EXAMPLE
To issue a POST request to the server, we use HttpClient service post() method.
HttpClient post() method has the following 3 parameters
url - the URL to which the data must be posted
body - the data to post to the server
options - Request options if any. On common piece of data that we send to the server using options parameter is the content-type header
employee.service.ts
insertEmployee(employee: Employee): Observable<Employee> {
if (employee.id === null) {
//Upon a successful post, the server returns an observable of the same type, in our case an Observable<Employee>.
//The returned Observable will contain the created employee object. This returned object will also have the values for server assigned properties
return this.httpClient.post<Employee>('http://localhost:3000/employees',employee, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}).pipe(catchError(this.handleError));
}
}
create-employee.component.ts
saveEmployee(empForm: NgForm): void {
this._employeeService.save(this.employee).subscribe(
(data: Employee) => {
console.log(data);
empForm.reset();
this._router.navigate(['list']);
},
(error: any) => { console.log(error); }
);
}
ANGULAR HTTPCLIENT PUT EXAMPLE
To issue a PUT request, we use HttpClient service put() method.
When an update is peformed our server side service does not return anything. When an item is updated, by default we get the http status code 204 no content
employee.service.ts
baseUrl = 'http://localhost:3000/employees';
updateEmployee(employee: Employee): Observable<void> {
return this.httpClient.put<void>(`${this.baseUrl}/${employee.id}`, employee, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}).pipe(catchError(this.handleError));
}
ANGULAR HTTPCLIENT DELETE EXAMPLE
delete() method does not return anything so we set the return type to void
employee.service.ts
baseUrl = 'http://localhost:3000/employees';
deleteEmployee(id: number): Observable<void> {
return this.httpClient.delete<void>(`${this.baseUrl}/${id}`).pipe(catchError(this.handleError));
}
display-employee.component.ts
deleteEmployee() {
this._employeeService.deleteEmployee(this.employee.id).subscribe(
() => console.log(`Employee with ID = ${this.employee.id} Deleted`),
(err) => console.log(err)
);
this.notifyDelete.emit(this.employee.id);
}