Conditional required validation in angular forms - javascript

If both fields dont have inputs then both can be optional. If firstname has input then set lastname as required. If lastname has input but firstname has no input then set firstname as required.
Set other field as required if one field has value and the other has not. Thank you.
So its like if i am the user and there are two fields which is firstname nad lastname. If i input data on firstname but lastname is empty then set the lastname as required (vice versa). Any idea ? thanks
#html code
<div fxLayout="row" fxLayoutGap="24px" formGroupName="person">
<div fxFlex fxLayout="row">
<mat-form-field appearance="outline" class="pr-4" fxFlex>
<mat-label>Firsname</mat-label>
<input matInput #firstname formControlName="firstname" trim required/>
<button type="button" mat-button *ngIf="firstname.value" matSuffix mat-icon-button aria-label="Clear"
(click)="clearFieldFirstname()">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</div>
<div fxFlex fxLayout="row">
<mat-form-field appearance="outline" class="pr-4" fxFlex>
<mat-label>Lastname</mat-label>
<input matInput #lastname formControlName="lastname" trim required/>
<button type="button" mat-button *ngIf="lastname.value" matSuffix mat-icon-button aria-label="Clear"
(click)="clearFieldLastname()">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</div>
</div>
#code
const personGroup = this.formBuilder.group({
firstName: [person.firstName, Validators.required],
lastName: [person.lastName, Validators.required],

Subscribe for the form control value changes and update the value and validity based on the condition.
this.person.get("firstname").valueChanges.subscribe(value => {
const lastName = this.person.get("lastname");
lastName.clearValidators();
if (value && !lastName.value) {
lastName.setValidators(Validators.required);
}
lastName.updateValueAndValidity({emitEvent : false});
});
this.person.get("lastname").valueChanges.subscribe(value => {
const firstName = this.person.get("firstname");
if (value && !firstName.value) {
firstName.setValidators(Validators.required);
} else {
firstName.clearValidators();
}
firstName.updateValueAndValidity({emitEvent : false});
});
Working Code
https://stackblitz.com/edit/angular-wfab5s?file=src%2Fapp%2Fapp.component.ts

Related

Custom Validations in Angular FormArray (Sum of proportion must be 100%)

I have created a formArray in angular to represent Nominee's distribution which has two fields Name and Proportion. Plus button(+) adds a new row and (X) button deletes current row. Now I want to code for a validation such that total of proportion column must be 100% (No matter how many nominees' name it consists)
Ex.
No Name Proportion
1 Andy 100
(Sum of proportion is 100)----------
Ex.2
No Name Proportion
1 Andy 60
2 Bruce 40 (Sum of proportion is 100)----------
Ex.3
No Name Proportion
1 Andy 60
2 Bruce 20
3 Ciao 20 (Sum of proportion is 100)----------
Here is my component.html code
<h5>Nominees</h5>
<div class="row">
<form novalidate [formGroup]="FormNominees">
<div clas="col-xs-12 form-group marL40">
<div formGroupName="itemRows">
<ng-container *ngIf="FormNominees.controls.itemRows!=null">
<div *ngFor="let itemrow of FormNominees.controls.itemRows.controls; let i = index"
[formGroupName]="i">
<div class="row">
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Name" formControlName="name">
<mat-error *ngIf="f9.name.touched && f9.name.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.name.touched && f9.name.errors?.pattern">Can only contain characters.
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Relationship"
formControlName="relationship">
<mat-error *ngIf="f9.relationship.touched && f9.relationship.errors?.required">It is mandatory
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<mat-select formControlName="gender" placeholder="Gender">
<mat-option value="Male">Male</mat-option>
<mat-option value="Female">Female</mat-option>
<mat-option value="Other">Other</mat-option>
</mat-select>
<mat-error *ngIf="f9.gender.touched && f9.gender.errors?.required">It is mandatory
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="phone" formControlName="phone">
<mat-error *ngIf="f9.phone.touched && f9.phone.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.phone.touched && f9.phone.errors?.pattern">Can only contain numbers.
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Proportion"
formControlName="gratuityProportion">
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.pattern">Can only contain numbers.
</mat-error>
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.proportionValidator">Total must be 100%.
</mat-error>
</mat-form-field>
<div class="col-sm-2.4">
<button (click)="deleteRow(i)" class="btn btn-danger">x</button>
<!-- </div>
<div class="form-group"> -->
<button type="button" (click)="addnewRow()"
[disabled]="FormNominees.invalid"
class="btn btn-primary">+</button>
</div>
</div>
</div>
</ng-container>
</div>
</div>
</form>
</div> <br>
Here is my component.ts code
import { proportionValidator } from './proportion.validator';
#Component({
selector: 'app-component',
templateUrl: './component.component.html',
styleUrls: ['./component.component.scss']
})
export class GratuityComponent implements OnInit {
FormNominees: FormGroup;
TotalRow: number;
itemFB: any;
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
this.FormNominees = this.fb.group({
itemRows: this.fb.array([this.initItemRow()])
});
initItemRow() {
this.itemFB = this.fb.group({
name: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9. ]*' )]],
relationship: ['', Validators.required],
phone: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9. ]*' )]],
gratuityProportion: ['', [Validators.required, Validators.pattern('^[0-9]*$'), proportionValidator] ],
gender:['', Validators.required],
employeeUniqueId: '00000001-0001-0001-0001-000000000001'
})
return this.itemFB;
}
addnewRow() {
const control = <FormArray>this.FormNominees.controls['itemRows'];
control.push(this.initItemRow())
}
deleteRow(index: number) {
const control = <FormArray>this.FormNominees.controls['itemRows'];
// control.push(this.initItemRow())
if (control != null) {
this.TotalRow = control.value.length;
}
if (this.TotalRow > 1) {
control.removeAt(index);
} else {
alert('One record is mandatory');
return false;
}
}
get f9() {return this.itemFB.controls;}
}
}
Now, my question is what code should I write in proportion.validator.ts to achieve the condition?
Straight from the docs:
const arr = new FormArray(
[
new FormControl('Nancy'),
new FormControl('Drew')
],
{
validators: myValidator //<- this is where your proportion validator goes
}
);
In your case, it looks like this:
this.FormNominees = this.fb.group({
itemRows: this.fb.array(
[
this.initItemRow()
],
{
validators: proportionValidator
}
)
});

AWS - Pull email template from S3 bucket using java/js

My question seems to be very similar to AWS - Using email template from S3 bucket except instead of Python, I am using java/springboot to send the email from AWS SES and using javascript/typescript for the front end. I'm able to send an email with a hardcoded subject and body. I want to be able to send an email with a template from my S3 bucket. My application has a list of the templates in the bucket. When selected, a preview is displayed. How can I send a selected template within my application?
# "send-email-templates.ts"
constructor(
private templateService: TemplateService,
private schoolService: SchoolService,
private hireStageService: HireStageService,
private emailSearchService: EmailSearchService,
private router: Router,
private _ngZone: NgZone,
private candidateService: CandidateService,
public dialog: MatDialog,
) { }
ngOnInit() {
this.getSchools();
this.getHireStages();
this.isPreviewOpen = false;
this.selectedTemplateName = null;
this.getAllTemplates();
}
triggerResize() {
this._ngZone.onStable.pipe(take(1)).subscribe(() => this.autosize.resizeToFitContent(true));
}
createNewTemp() {
this.router.navigate([`/create_template`])
}
// Send email template to multiple emails after search filter
sendEmail() {
this.dialog.open(DialogOverviewExampleDialog, {
width: '250px'
});
let candidateEmails = (<HTMLInputElement>document.getElementById("emailList")).value
let subject = this.sendForm.get('subjectForm').value
let body = "HARDCODED BODY"
this.candidateService.sendEmailWithoutPositions(candidateEmails, subject, body).subscribe(() => {
this.router.navigate(['/send_email']);
});
}
searchCandidates() {
this.emailSearchService.searchCandidates(this.searchForm.get('namesForm').value,
this.searchForm.get('majorsForm').value, this.schools.toString(),
this.hireStages.toString(), this.searchForm.get('startDateForm').value,
this.searchForm.get('endDateForm').value)
.subscribe(listOfEmails => {
console.log(listOfEmails);
(<HTMLInputElement>document.getElementById('emailList')).value = <string> listOfEmails;
})
}
//Send email to new candidate
sendEmail(candidateEmail: string, positionId: number[], subject: string, body: string) {
let details:emailDetails = new emailDetails();
details.to = candidateEmail;
details.positionId = positionId;
details.subject = subject;
details.body = body;
return this.http.post<emailDetails>(`${this.context.getOutgoingEmailUrl()}`, details)
}
// Send email to string of search/filtered candidate emails
sendEmailWithoutPositions(candidateEmails: string, subject: string, body: string) {
let details:emailDetails2 = new emailDetails2();
details.to = candidateEmails;
details.subject = subject;
details.body = body;
return this.http.post<emailDetails2>(`${this.context.getOutgoingEmailUrl()}`, details)
}
HTML
<mat-grid-tile colspan="6" rowspan="4">
<div class='search'>
<form [formGroup]='searchForm' autocomplete='off' novalidate>
<mat-form-field class="nameSearch">
<span matPrefix>Name: </span>
<input matInput id='namesForm' placeholder=" Enter candidate name" formControlName='namesForm'>
</mat-form-field>
<mat-form-field class="majorSearch">
<span matPrefix>Major: </span>
<input matInput id='majorsForm' placeholder=" Enter candidate major" formControlName="majorsForm">
</mat-form-field>
<tr>
<td>
<mat-form-field *ngIf="schoolSource" class="schools">
<mat-label>Schools</mat-label>
<mat-chip-list #chipList>
<mat-chip *ngFor="let school of schools" [selectable]="true" [removable]="true"
(removed)="removeSchool(school)">
{{ school }}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input input='schoolsForm' placeholder="Choose school(s)" #schoolInput [formControl]="schoolCtrl"
[matAutocomplete]="auto" [matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur" formControlName='schoolsForm'>
</mat-chip-list>
<mat-autocomplete #auto (optionSelected)="selectedSchool($event)">
<mat-option *ngFor="let school of schoolSource" [value]='schoolId'>
{{ school.schoolName }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</td>
<td>
<mat-form-field *ngIf="hireStageSource" class="hireStages">
<mat-label>Hire Stage</mat-label>
<mat-chip-list #chipList1>
<mat-chip *ngFor="let hireStage of hireStages" [selectable]="true" [removable]="true"
(removed)="removeStage(hireStage)">
{{ hireStage }}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input id='stagesForm' placeholder="Choose Hire Stage(s)" #hireStageInput [formControl]="hireStageCtrl"
[matAutocomplete]="auto1" [matChipInputFor]="chipList1"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur" formControlName='stagesForm'>
</mat-chip-list>
<mat-autocomplete #auto1 (optionSelected)="selectedStage($event)">
<mat-option *ngFor="let hireStage of hireStageSource" [value]='stageId'>
{{ hireStage.stageName }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<tr>
<div class="dates">
<mat-form-field class="startDate">
<input matInput id='startDateForm' [matDatepicker]="picker" placeholder="Start date"
name="startDate" formControlName='startDateForm'>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-form-field class="endDate">
<input matInput id='endDateForm'[matDatepicker]="picker2" placeholder="End date"
name="endDate" formControlName='endDateForm'>
<mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker #picker2></mat-datepicker>
</mat-form-field>
<button class="addEmail" mat-raised-button color = "primary" (click) = "searchCandidates()">Add</button>
</div>
</tr>
</td>
</form>
</div>
</mat-grid-tile>
<mat-grid-tile colspan="4" rowspan="4">
<div class='send'>
<form [formGroup]='sendForm' autocomplete='off' novalidate name="form">
<tr>
<button class="newTemplateButt" mat-raised-button color = "primary" (click) = "createNewTemp()">Create New Template</button>
</tr>
<tr>
<mat-form-field class="sendToList">
<mat-label>Candidate Emails will be listed here</mat-label>
<textarea id = 'emailList'
matInput
cdkTextareaAutosize
#autosize = "cdkTextareaAutosize"
cdkAutosizeMinRows = "1"
cdkAutosizeMaxRows = "8"></textarea>
</mat-form-field>
</tr>
<tr>
<mat-form-field class = "subjectLine">
<span matPrefix>Subject: </span>
<input matInput id='subjectLine' formControlName='subjectForm'>
</mat-form-field>
</tr>
<button mat-raised-button color = "primary" class = "sendEmail" (click) = "sendEmail()">Send Email</button>
</form>
</div>
</mat-grid-tile>
</mat-grid-list>
<div class="email-templates" [style.display]="isPreviewOpen ? 'grid' : 'flex'">
<app-email-templates-list [templates]="templateList" [selectedTemplate]="selectedTemplateName" (display)="displayPreviewHandler($event)"></app-email-templates-list>```
<app-email-templates-preview [previewStatus]="isPreviewOpen" [selectedTemplate]="selectedTemplateName"></app-email-templates-preview>
</div>
sendEmail() {
this.dialog.open(DialogOverviewExampleDialog, {
width: '250px'
});
let candidateEmails = (<HTMLInputElement>document.getElementById("emailList")).value
let subject = this.sendForm.get('subjectForm').value
console.log(this.selectedTemplateName)
this.templateService.getTemplate(this.selectedTemplateName).subscribe((templateData : any) => {
console.log(templateData.Body)
console.log(templateData.Body.data)
let body = importTemplate(templateData.Body.data).toString();
console.log(body)
this.candidateService.sendEmailWithoutPositions(candidateEmails, subject, body).subscribe(() => {
this.router.navigate(['/send_email']);
});
})
}
This is now sending the templates, but images are failing to display. Still looking into that issue.
Edit: The images weren't sending because they were base64 encoded. When viewing the source of all images in google images search results, they all showed base64 encoding. I noticed that when I went to the source of these images, it would give me a more specific src such as https://......jpg. When I dragged and dropped this source image into my template and saved and sent, it forwent the base64 encoding and therefore, images show up in my Gmail inbox when sent from my application now.

Form control problem with angular Material

I have this message appear in my console when I want edit my user.
I see some solution on the web, Some people have trouble with it but no solution for me.
Does someone have the same problem ?
EditUserDialogComponent.html:37 ERROR Error:
ngModel cannot be used to register form controls with a parent formGroup directive. Try using
formGroup's partner directive "formControlName" instead. Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>
In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});
Or, if you'd like to avoid registering this form control, indicate that it's standalone in ngModelOptions:
Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
<input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
</div>
at Function.modelParentException (forms.js:6182)
at NgModel._checkParentType (forms.js:6754)
at NgModel._checkForErrors (forms.js:6740)
at NgModel.ngOnChanges (forms.js:6640)
at checkAndUpdateDirectiveInline (core.js:31905)
at checkAndUpdateNodeInline (core.js:44366)
at checkAndUpdateNode (core.js:44305)
at debugCheckAndUpdateNode (core.js:45327)
at debugCheckDirectivesFn (core.js:45270)
at Object.eval [as updateDirectives] (EditUserDialogComponent.html:42)
and this is my HTML,
I think it's a problem about the form control, but if I follow the instructions of the error message,
I have the same...
<div class="manage-content">
<div class="title">
<mat-icon class="user-icon">how_to_reg</mat-icon>
<h3>Edit a user</h3>
</div>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field class="full-width-input">
<input id="firstName" matInput placeholder="First name" formControlName="f_name" [(ngModel)]="selectedUser.f_name"#f_name>
<mat-error *ngIf="isFieldInvalid('f_name')">
The first name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="middleName" matInput placeholder="Middle name" formControlName="m_name" [(ngModel)]="selectedUser.m_name" #m_name>
<mat-error *ngIf="isFieldInvalid('m_name')">
The middle name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="lastName" matInput placeholder="Last name" formControlName="l_name" [(ngModel)]="selectedUser.l_name" #l_name>
<mat-error *ngIf="isFieldInvalid('l_name')">
The last name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="email" matInput placeholder="Email" formControlName="email" [(ngModel)]="selectedUser.email" #email>
<mat-error *ngIf="isFieldInvalid('email')">
The email you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<div class="visibility">
<input id="password" matInput type="password" placeholder="Password" formControlName="password" [(ngModel)]="selectedUser.password">
<mat-icon *ngIf="isPasswordVisible" (click)=showPassword()>visibility</mat-icon>
<mat-icon *ngIf="!isPasswordVisible" (click)="showPassword()">visibility_off</mat-icon>
</div>
<mat-error *ngIf="isFieldInvalid('password')">
The password you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Role" class="full-width-input" [(ngModel)]="selectedUser.role">
<mat-option value="option">End-User</mat-option>
<mat-option value="option">View-User</mat-option>
<mat-option value="option">Vendor</mat-option>
<mat-option value="option">Tool Guy</mat-option>
</mat-select>
</mat-form-field>
<div class="cta-btn">
<button mat-raised-button class="createUserBtn" color="primary" type="submit">Update user</button>
<button mat-raised-button class="createUserBtn" color="warn" type="submit" (click)="click()">Cancel</button>
</div>
</form>
</div>
#Component({
selector: 'app-edit-user-dialog',
templateUrl: 'edit-user-dialog.component.html',
styleUrls: ['./manage-user.component.scss']
})
export class EditUserDialogComponent {
form: FormGroup;
formSubmitAttempt: boolean;
isPasswordVisible = false;
selectedUser: User;
constructor(private formBuilder: FormBuilder, private userService: UserService, public dialog: MatDialog, public dialogRef: MatDialogRef<EditUserDialogComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) {
this.selectedUser = data;
console.log(this.selectedUser);
this.form = this.formBuilder.group({
email: ['', Validators.compose([Validators.required, Validators.pattern('^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$')])],
password: ['', Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(35)])],
f_name: ['', Validators.compose([Validators.required, Validators.maxLength(100)])],
m_name: ['', Validators.compose([Validators.maxLength(100), Validators.pattern('[0-9]+')])],
l_name: ['', Validators.compose([Validators.required, Validators.maxLength(100)])]
// client: ['', Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(35)])],
});
}
click(): void {
this.dialogRef.close();
}
showPassword() {
const pass: HTMLInputElement = document.getElementById('password') as HTMLInputElement;
if (pass.type === "password") {
this.isPasswordVisible = true;
pass.type = "text";
} else {
this.isPasswordVisible = false;
pass.type = "password";
}
}
isFieldInvalid(field: string) {
return (
(!this.form.get(field).valid && this.form.get(field).touched) ||
(this.form.get(field).untouched && this.formSubmitAttempt)
);
}
onSubmit() {
// if (this.form.valid) {
// this.authService.login(this.form.value);
// }
this.formSubmitAttempt = true;
}
}
thanks a lot for your help
The issue you are having is the usage of the ([ngModel]) inside of a formGroup.
If you need to extract a value, you will have to do it via the formGroup variable on the TS component.
Check out more information on the Angular support page: https://angular.io/guide/reactive-forms
change this:
`<input id="password" matInput type="password" placeholder="Password" formControlName="password" [(ngModel)]="selectedUser.password">`
into this:
`<input id="password" matInput type="password" placeholder="Password" formControlName="password" (change)="selectedUserChanged($event)">`
//and add to your ts
`selectedUserChanged(change: any) {
this.group.get('password').setValue(change.value);
}`

How to dynamically add validations to material forms in Angular (v6)?

I have a form whose field names and required validators will change based on the form type (content_type) selected. On selecting the form_type, I get a list of required_fields and optional_fields, accordingly, I need to add Validators.required, so i can disable the submit button if the form is not filled properly.
After adding logic, i get following errors:
this.form._updateTreeValidity is not a function
and
this.form.get is not a function
and the form ends up looking empty with the following errors. If i just keep fields without any formControlName or validations it looks perfectly fine.
Code
Sample form fields
formFields = {
required_fields: {
actual_content: "Content",
name: "Name",
contact: "Phone Number"
},
optional_fields: {
additional_content: "Additional card content",
website: "Website URL",
}
};
form.component.ts
formGroup: FormGroup;
ngOnInit() {
this.selectedType = 'text';
this.service.getFormFields(this.selectedType)
.subscribe((data: {required_fields, optional_fields }) => {
// create array of field names to display in template
this.fieldNames = Object.assign(data.required_fields, data.optional_fields);
// create dynamic formGroup, with proper validators
this.formGroup = this.createGroup(data);
console.log({FormGroup: this.formGroup});
});
}
createGroup(formFields): FormGroup {
const group: any = {};
// required fields
const reqFields = Object.keys(formFields.required_fields);
reqFields.forEach(field => {
group[field] = new FormControl(``, [Validators.required]);
});
// optional fields
const optFields = Object.keys(formFields.optional_fields);
optFields.forEach(field => {
group[field] = new FormControl(``);
});
return group;
}
// the dynamically generated formGroup might look like
// formGroup = new FormGroup({
// actual_content: new FormControl(``, [Validators.required]),
// name: new FormControl(``, [Validators.required]),
// contact: new FormControl(``, [Validators.required]),
// additional_content: new FormControl(),
// website: new FormControl(),
// })
form.component.html
<form novalidate class="mainForm" [style.fontSize.px]="15" [formGroup]="formGroup" #myForm="ngForm">
<div>
<h3 class="sHeading"> Select Form Type </h3>
<mat-form-field appearance="outline" class="formField">
<mat-select placeholder="Select Form Type" formControlName="content_type">
<mat-option
#formType
class="option"
*ngFor="let type of formTypes"
[value]="type.type"
(click)="updateFormType(formType.value)">
{{type.type}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.name}} </h3>
<mat-form-field appearance="outline" class="formField">
<input matInput
placeholder="Name should have 3 characters min."
formControlName="name">
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.content_url}} </h3>
<mat-form-field appearance="outline" class="formField">
<input matInput
placeholder="Provide Valid URL"
formControlName="content_url">
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.actual_content}} </h3>
<mat-form-field appearance="outline" class="formField">
<textarea
matInput
placeholder="Describe the content"
rows=6
formControlName="actual_content"></textarea>
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.additional_content}} </h3>
<mat-form-field appearance="outline" class="formField">
<textarea
matInput
placeholder="Describe the additional content"
rows=6
formControlName="additional_content"></textarea>
</mat-form-field>
</div>
<div>
<button mat-raised-button type="submit" class="submitBtn" [disabled]="!myForm.valid">Submit</button>
</div>
</form>
Question
What is the correct way to add validationss dynamically?
Thanks in advance.

Form Validation between multiple input fields

So I have a form with multiple input form fields,and a nested form group. In the nested formgroup, the validation should be such that if any one of the three inputs is filled, then the form should be valid. The person can fill either all, or even just one and the form should be valid. My template code is as follows:
<h3 class="form-title">{{title}}</h3>
<form [formGroup]="formX" novalidate>
<div formGroupName="brandIdentifier">
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="brandName.valid && brandName.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="brandName.invalid && brandName.touched">close</mat-icon>
<input matInput type="text" placeholder="Brand Name" formControlName="brandName" />
</mat-form-field>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="brandId.valid && brandId.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="brandId.invalid && brandId.touched">close</mat-icon>
<input matInput type="text" placeholder="Brand Id" formControlName="brandId" />
</mat-form-field>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="contentId.valid && contentId.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="contentId.invalid && contentId.touched">close</mat-icon>
<input matInput type="text" placeholder="Content Id" formControlName="contentId" />
</mat-form-field>
</div>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="startTime.valid && startTime.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="startTime.invalid && startTime.touched">close</mat-icon>
<input matInput type="text" placeholder="Start Time" formControlName="startTime" />
</mat-form-field>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="endTime.valid && endTime.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="endTime.invalid && endTime.touched">close</mat-icon>
<input matInput type="text" placeholder="End Time" formControlName="endTime" />
</mat-form-field>
<button mat-raised-button color="primary" (click)="startAnalysis()" [ngClass]="{'form-valid':formX.valid, 'form-invalid':formX.invalid}">Submit</button>
<pre>{{formX.value | json}}</pre>
</form>
How would I go about this? using Validator class is a given, but I'm unable to get it optional.
You can use a custom validator for this, apply the custom validator for the nested group, where we check that at least one of the three fields has a value else than empty string, so here's a sample:
Build form:
this.myForm = this.fb.group({
nestedGroup: this.fb.group({
first: [''],
second: [''],
third: ['']
// apply custom validator for the nested group only
}, {validator: this.myCustomValidator})
});
Custom validator:
myCustomValidator(group: FormGroup) {
// if true, all fields are empty
let bool = Object.keys(group.value).every(x => group.value[x] === '')
if(bool) {
// return validation error
return { allEmpty: true}
}
// valid
return null;
}
Then in your form you can show the validation message by:
<small *ngIf="myForm.hasError('allEmpty','nestedGroup')">
Please fill at least one field
</small>
Finally a DEMO :)
Please follow below steps:
Note: we are just giving idea how you can solve your problem.
import FormsModule, ReactiveFormsModule in module.ts
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [FormsModule, ReactiveFormsModule]})
Modify your html as given e.g.
User name
User name is required.
User name must be at least 4 characters long.
Password
Password is required.
Password must be at least 6 characters long.
Forget the password ?
<div class="form-group">
<button type="submit" (click)='onSubmit()' dir-btn-click [disabled]="!loginForm.valid" class="btn btn-inverse btn-block">Sign in</button>
</div>
</form>
Modify code as below in [yourcomponent].ts e.g.:
import { Component } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
loginForm: FormGroup;
constructor( private fb: FormBuilder) {
this.crateLoginForm()
}
get userName() { return this.loginForm.get('userName'); }
get password() { return this.loginForm.get('password'); }
crateLoginForm() {
this.loginForm = this.fb.group({
userName: ['', [Validators.required, Validators.minLength(4)]],
password: ['', [Validators.required, Validators.minLength(6)]]
})
}
onSubmit() {
let userName= this.loginForm.value.userName;
let pwd =this.loginForm.value.password;
}
}

Categories