AbstractControl.value[data] is retuning undefined in Custom Validator using AbstractControl? - javascript

I'm trying to write cross-field -validation using Abstract Control.
control.value[start] is showing undefined. kindly help me on solving.
working code is here.
app.component.html
export class AppComponent {
form: FormGroup;
rangeStart = new FormControl('', {
validators: [this.MyAwesomeRangeValidator('rangeStart')],
});
rangeEnd = new FormControl('', {
validators: this.MyAwesomeRangeValidator('rangeEnd'),
});
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
rangeStart: this.rangeStart,
rangeEnd: this.rangeEnd,
});
}
MyAwesomeRangeValidator(rangeStart: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const start = control.value[rangeStart];
const end = control.value;
console.log('start', start, 'end', end);
let re = /^[1-9]\d*$/;
if (re.test(start) && re.test(end)) {
return null;
} else if (re.test(start) && !re.test(end)) {
return null;
} else if (!re.test(start) && re.test(end)) {
return null;
} else {
return {
range: true
};
}
};
}
}
<form [formGroup]="form">
<input formControlName="rangeStart" placeholder="Range start" type="number">
<input formControlName="rangeEnd" placeholder="Range end" type="number">
</form>
<div> Valid: {{ form.valid ? '👍' : '👎' }} </div>
working code

You are trying to get property 'rangeStart' from string.
const start = control.value[rangeStart];, where control.value is a string. If you want to validate few fields at once then you should put validator to the FormGroup.
export class AppComponent {
form: FormGroup;
rangeStart = new FormControl('', {
validators: [this.MyAwesomeRangeValidator('rangeStart', 'rangeEnd')],
});
rangeEnd = new FormControl('', {
validators: this.MyAwesomeRangeValidator('someField_1', 'someField_2'),
});
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
rangeStart: this.rangeStart,
rangeEnd: this.rangeEnd,
});
}
MyAwesomeRangeValidator(
startControlName: string,
endControlName: string
): ValidatorFn {
return (formGroup: FormGroup): ValidationErrors | null => {
const start = formGroup.get(startControlName).value;
const end = formGroup.get(endControlName).value;
console.log(formGroup, 'start', start, 'end', end);
let re = /^[1-9]\d*$/;
if (!re.test(start) && !re.test(end)) {
return { range: true };
}
return null;
};
}
}
UPD: added dynamic form controls names

Related

ERROR TypeError: Cannot read properties of undefined (reading 'file')

I make this component with custom validate and declare variable in top of component and assigning value to , but when i use this variable inside custom validation it return this error if change value in form "ERROR TypeError: Cannot read properties of undefined (reading 'file')"
in custom valide extention file I have to declare and assign value to variable inside '
so I want to know the reason of that
is custom validate fun not observe so it excute before other fun?,
but why variable value donot existing and assigning to undefiend,
and how i use file size in validate file size?
in all conditions this is code of component
import { Component, ElementRef, OnInit, ViewChild } from '#angular/core';
import { AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, Validators } from '#angular/forms';
import { PagedResult } from 'src/app/core/models/pagedResult.model';
import { AlertService } from 'src/app/core/services/alert.service';
import { FakeDataService } from 'src/app/core/services/fake-data.service';
import { Employee } from '../../models/employee.model';
import { PenaltyReason } from '../../models/penalty-reason.model';
import { PenaltyType } from '../../models/penalty-type.model';
import { Penalty } from '../../models/penalty.model';
#Component({
selector: 'app-penalties',
templateUrl: './penalties.component.html',
styleUrls: ['./penalties.component.css']
})
export class PenaltiesComponent implements OnInit {
penalties: PagedResult<Penalty>;
page = 1;
formTitle = "اضافة عقوبة";
penaltyTypes: PenaltyType[];
penaltyReasons: PenaltyReason[];
employees: Employee[];
selectedPenaltyIndex: number;
penaltyForm: FormGroup;
file: FormControl;
constructor(private fakeData: FakeDataService, private alert: AlertService) { }
loadData() {
this.fakeData.getDataFor('Penalties', this.page).then(result => {
this.penalties = result as PagedResult<Penalty>;
});
}
loadRequiredData() {
//load penalty Types
this.fakeData.getAllDataFor('PenaltyTypes').then(result => {
this.penaltyTypes = result as PenaltyType[];
});
// Loading penalty Reasons
this.fakeData.getAllDataFor('penaltyReasons').then(result => {
this.penaltyReasons = result as PenaltyReason[];
});
// Loading Employees list
this.fakeData.getAllDataFor('Employees').then(result => {
this.employees = result as Employee[];
});
}
ngOnInit(): void {
this.resetForm();
this.loadData();
this.loadRequiredData();
}
paginateData(page: number) {
this.page = page;
this.loadData();
}
showEditForm(id: number, index: number) {
var penalty2Edit = this.penalties?.data?.find(p => p.Id == id) as Penalty;
this.penaltyForm?.setValue({ ...penalty2Edit });
this.selectedPenaltyIndex = index;
}
delete(id: number) {
var itemIndex = this.penalties.data.findIndex(c => c.Id == id);
this.penalties.data.splice(itemIndex, 1);
this.alert.showSuccess('تم مسح العنصر بنجاح');
}
resetForm() {
this.file = new FormControl(null);
this.penaltyForm = new FormGroup({
Id: new FormControl(0),
FingerPrintId: new FormControl(0),
EmpId: new FormControl(0),
EmpName: new FormControl('', Validators.required),
Date: new FormControl('', Validators.required),
PenalityTypeId: new FormControl(0),
PenalityTypeText: new FormControl('', Validators.required),
DeductionDaysCount: new FormControl(0, Validators.required),
ReasonTypeId: new FormControl(0, Validators.required),
DisciplineNotes: new FormControl('', Validators.required),
ImageURL: new FormControl('', [this.ValidateExtentionFile, this.validateFileSize])
});
this.loadRequiredData();
}
addPenalty() {
this.penaltyForm.get('Id')?.setValue(Math.floor(Math.random() * 1000));
this.penalties.data.unshift(this.penaltyForm.value);
this.resetForm();
this.alert.showSuccess("تم إضافةالعقوبة بنجاح");
}
updatePenalty() {
this.penalties.data.splice(this.selectedPenaltyIndex, 1, this.penaltyForm.value);
this.alert.showSuccess('تم تعديل العنصر بنجاح');
}
// deal with file
onFileSelected(event: any) {
this.file.setValue(event.target.files[0]);
console.log(this.file);
}
ValidateExtentionFile(control: AbstractControl): ValidationErrors | null {
if (!control.value)
return null;
let allowedEXT = ['jpg', 'png', 'pdf', 'jpeg'];
var fileExt = control.value?.split('.').pop().toLowerCase();
if (allowedEXT.includes(fileExt))
return null;
return { valideFileEXT: true };
}
validateFileSize(control: AbstractControl): ValidationErrors | null {
let maxSize = 1;
if (!control.value || !this.file)
return null;
const fileSize = (this.file.value.size / 1024 / 1024); // convert to MB
if (fileSize > maxSize) {
return { 'validateFileSize': true };
}
return null;
}
}
you can use like this
in the component file:
handleFileInput(files: any) {
this.fileNameDetail = undefined;
const image = files.files.item(0);
const reader = new FileReader();
reader.addEventListener("load", () => {
this.imageUrl = reader.result;
}, false);
reader.readAsDataURL(image);
}
in the template file:
<button (click)="filePicker.click()" class="file-picker-opener">انتخاب فایل</button>
<input type="file"
class="form-control"
#filePicker
accept="image/jpeg,image/jpg"
id="file"
(change)="handleFileInput($event.target);"
/>

Angular Material datepicker custom date(start and end date ) validation issue

I have two dates like one arrivaldate and exitdate .Trying to add custom validation like the exit date not be less than arrival date if its less show error message.Please find the below code.
component.ts file:
arrivalDate: ['', [Validators.required, this.validateArrivalDate()]],
exitDate: ['', [Validators.required, this.validateExitDate()]],
validateExitDate(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
if (this.currentTravelFormGroup !== undefined) {
//const arrivalDate = control.value;
const exitDate = this.currentTravelFormGroup.controls['exitDate'].value;
const arrivalDate = this.currentTravelFormGroup.controls['arrivalDate'].value
if (exitDate <= arrivalDate) return { requiredToDate: true };
}
};
}
validateArrivalDate(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
if (this.currentTravelFormGroup !== undefined) {
const exitDate = this.currentTravelFormGroup.controls['exitDate'].value;
const fexitDate = new Date(exitDate);
const arrivalDate = this.currentTravelFormGroup.controls['arrivalDate'].value;
if (fexitDate <= arrivalDate) return { requiredFromDate: true };
}
};
}
In Html I'm showing error message:
<mat-error *ngIf="currentTravelFormGroup.get('arrivalDate').hasError('requiredFromDate')">Please provide a valid arrival date</mat-error>
<input class="form-control bgColor" [matDatepicker]="pickerExitDateFromGermany" placeholder="MM/DD/YYYY" [min]="minStartDateFutureTravel"  [max]="maxStartDateFutureTravel" formControlName="exitDate" id="exitDate" readonly (click)="pickerExitDateFromGermany.open()"
[ngClass]="{ 'is-invalid': submitted && travelDet.exitDate.errors }">
<mat-datepicker #pickerExitDateFromGermany></mat-datepicker>
<mat-error *ngIf="currentTravelFormGroup.get('exitDate').hasError('requiredToDate')">Please provide a valid exitdate</mat-error>
The condition works and shows the error message respectively for exit and arrival date but.
if arrival date is 11/11/2019 and exit date is 10/11/2019(error message will be shown below exit input field).If i change arrival date as 08/11/2019 (arrival date
what is the problem .how do i solve it.Please help
I solved this problem by clear ,setting and updating values after validateExitDate() and validateArrivalDate() methods.
validateExitDate(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
if (this.currentTravelFormGroup !== undefined) {
//const arrivalDate = control.value;
const exitDate = this.currentTravelFormGroup.controls['exitDate'].value;
const arrivalDate = this.currentTravelFormGroup.controls['arrivalDate'].value
if (exitDate <= arrivalDate) return { requiredToDate: true };
else{
this.currentTravelFormGroup.get('exitDate ').clearValidators();
this.currentTravelFormGroup.get('exitDate').updateValueAndValidity();
}
}
};
}
Same with Arrivaldate function.:)

Add/ remove custom validators to form array in angular 8

Hi I am trying to set/ take away custom validators for different elements in a form array that can change around, so far what I have tried to do is create a switch statement and loop through all of the input types that are set so I could set the validation rule as well as send a message to the user if the rule isn't met. The problem I am having is the form is initialized before the form data is set.
So my question is how can I loop through the array and set the validation rules. If someone could let me know if im along the right tracks with using a switch statement but have code in the wrong place or if there is a different and better approach it would be most helpful thank you
export class ReactiveComponent implements OnInit {
public form: FormGroup;
public fieldList: any;
types: Array<any>;
formData: any;
Param: string;
setData: any;
formLabelNames: any;
get contactFormGroup() {
return this.form.get('inputs') as FormArray;
}
constructor(
private route: ActivatedRoute,
private fb: FormBuilder,
private api: FormService,
private notifiy: NotificationService,
private auth: AuthService,
private router: Router) { }
ngOnInit() {
this.form = this.fb.group({
name: ['', Validators.compose([Validators.required])],
organization: ['', Validators.compose([Validators.required])],
inputs: this.fb.array([this.createForm()])
});
this.route.paramMap.subscribe(params => {
this.Param = params.get('id');
this.getForm(this.Param);
});
// set fieldslist to this field
this.fieldList = this.form.get('inputs') as FormArray;
}
// formgroup
createForm(): FormGroup {
return this.fb.group({
type: ['', Validators.compose([Validators.required])],
name: ['', Validators.compose([Validators.required])],
value: ['', this.validators()]
});
}
getForm(id) {
this.api.getForm(id).subscribe(
(data: any) => this.setForm(data)
);
}
getFieldsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
getContactsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
setForm(data) {
const d = data.results;
this.setData = d;
this.formLabelNames = d[0].fields;
this.form.patchValue({
name: [d[0].form_name],
organization: [d[0].org],
});
this.form.setControl('inputs', this.setExistingFields(d[0].fields));
}
setExistingFields(fields: any): FormArray {
const formArray = new FormArray([]);
this.fieldList = formArray;
fields.forEach(f => {
formArray.push(this.fb.group({
name: f.name,
type: f.type,
value: f.value
}));
});
return formArray;
}
/* This is where I have tried to create a switch statement but I get a undefined error because the setform function is being called after this one */
validators() {
this.formLabelNames.type.forEach((field: any) => {
switch (field.type) {
case 'email':
}
});
}
submit() {
if (this.form.valid) {
const formId = this.Param;
const local = this.auth.decodePayload();
const userId = local.sub;
this.router.navigateByUrl('/dashboard');
this.api.sendForm(this.form.value, formId, userId).subscribe();
this.form.reset();
} else {
this.notifiy.showFailure('Form is not valid', 'Error');
}
}
}
You have several problems, as far as I can judge:
You don't initialize neither the formControls nor the formArray correctly inside of you formGroup. It should rather look like this:
this.form = this.fb.group({
name: new FormControl( "", {validators: [Validators.required]}),
organization: new FormControl( "", {validators: [Validators.required]}),
inputs: this.fb.array([new FormControl(), new FormControl()]),
});
Besides: What is the point of using a formArray when it consists of only one formGroup?
Off course you can set the validators for any abstractControl be it a formControl or a formGroup. For the array it would look like something like this:
this.formArray.controls.map((ctrl) => {
ctrl.setValidators([Validators.required, Validators.email]);
});
It doesn't matter where in the component class you put your method. Though it surely matters when that method is invoked!

MomentJS changing value when assigned to new variable

When I try assigning a moment to a new variable it changes the value without me modifying it.
I have tried everything from forcing the use of UTC and settings timezones. It just keeps changing the value.
#Component({
selector: 'app-appointment-create',
templateUrl: './appointment-create.component.html',
styleUrls: ['./appointment-create.component.css']
})
export class AppointmentCreateComponent implements OnInit {
appointment: CreateAppointmentDto;
form: FormGroup;
private formSubmitted: boolean;
tasks: Task[];
availability: Availability[];
task: number;
availablemoments: Moment[];
constructor(
private titleService: Title,
private router: Router,
private appointmentService: AppointmentService,
private taskService: TasksService,
private webLogger: WebloggerService,
private availabilityService: AvailabilityService,
) {
this.appointment = new CreateAppointmentDto();
}
dateFilter = (d: Moment): boolean => {
return this.availability.filter(s => s.timestampstart.isSame(d, 'day')).length >= 1;
}
ngOnInit() {
this.titleService.setTitle('New Appointment');
this.taskService.getActiveTasks().subscribe(value => {this.tasks = value; });
this.form = new FormGroup({
timestampstart: new FormControl(this.appointment.timestampstart, Validators.required),
daystart: new FormControl(this.appointment.timestampstart, Validators.required),
location: new FormControl(this.appointment.location, Validators.required),
description: new FormControl(this.appointment.description, Validators.required),
paid: new FormControl(false, Validators.required),
serviceType: new FormControl(this.appointment.serviceTypeId, Validators.required),
client: new FormControl(this.appointment.clientId, Validators.required),
assignedUser: new FormControl(this.appointment.assignedUserId, Validators.required),
});
}
onSubmit(event) {
event.preventDefault();
this.formSubmitted = true;
if (this.form.valid) {
this.form.disable();
this.appointment.timestampstart = this.form.get('timestampstart').value;
this.appointment.location = this.form.get('location').value;
this.appointment.description = this.form.get('description').value;
this.appointment.paid = this.form.get('paid').value;
this.appointment.serviceTypeId = this.form.get('serviceType').value;
this.appointment.clientId = this.form.get('client').value;
this.appointment.assignedUserId = this.form.get('assignedUser').value;
this.appointmentService.createNewAppointment(this.appointment)
.subscribe(value => { this.router.navigate([`/dashboard/appointment/${value.id}/edit`]); });
} else {
this.webLogger.error('The form is invalid, please check the values');
}
}
selectTask($event: Event) {
this.task = Number(this.form.get('serviceType').value);
this.availabilityService.getAvailabilityForTask(this.task).subscribe(value => {
this.availability = value;
});
}
setTime($event: Event) {
this.availablemoments = [];
const dayAvailability: Availability[] = this.availability.filter(
s => s.timestampstart.isSame(moment(this.form.get('daystart').value), 'day'));
const currentDate = dayAvailability.reduce((prev, curr) => prev.timestampstart < curr.timestampstart ? prev : curr).timestampstart;
dayAvailability.forEach(value => {
while (value.timestampend.isAfter(currentDate)) {
if (!this.availablemoments.includes(moment(currentDate))) {
this.availablemoments.push(moment(currentDate));
}
currentDate.add(30, 'minutes');
}
});
}
}
this.availability is a list of Availability objects which include start and end moments
I expect the second console log to return the same as the first console log.
UPDATE:
The Availability class looks like this:
export class Availability {
id: number;
timestampstart: Moment;
timestampend: Moment;
location: string;
description: string;
paid: boolean;
payment: Invoice;
serviceType: Task;
client: Client;
assignedUser: User;
static serialize(data: any): Availability {
const user: Availability = Object.assign(new this(), data);
if (data.hasOwnProperty('timestampstart')) {
user.timestampstart = moment(data.timestampstart);
}
if (data.hasOwnProperty('timestampend')) {
user.timestampend = moment(data.timestampend);
}
if (data.hasOwnProperty('serviceType')) {
user.serviceType = Task.serialize(data.serviceType);
}
if (data.hasOwnProperty('client')) {
user.client = Client.serialize(data.client);
}
if (data.hasOwnProperty('assignedUser')) {
user.assignedUser = User.serialize(data.assignedUser);
}
return user;
}
}
I figured it out, since I was using Angular, I assigned the formControlName to the wrong HTML tag (the option tag instead of the select) causing the get of the value to return a value that I wasn't expecting. I've updated the main post to show the entire code.

convert and concatenate multiple FormGroup types to object type in Angular 5

Im using mat-stepper of Angular Material design library.
I use 3 separate FormGroups. I would send informations to database using httpClient method, for that I have created an interface :
export interface NouveauProjet {
leadProj: String ;
nomProj: String;
descProj: String;
besProj: Number;
pers: [Personnes];
Backlog: [Fonctionnalite]
}
export interface Personnes {
name: String;
poste:String
}
export interface Fonctionnalite {
fonctionnalite: String;
userStory: String
}
In my component file I create forms and nouveauProject variable that will contain my values .
export class AjoutProjetComponent implements OnInit {
isLinear = false;
firstFormGroup: FormGroup;
secondFormGroup: FormGroup;
thirdFormGroup: FormGroup;
nouveauProjet: NouveauProjet;
constructor(
private _formBuilder: FormBuilder,
private ajoutProj: AjoutprojService
) {}
ngOnInit() {
console.log();
this.firstFormGroup = this._formBuilder.group({
leadProj: ["", Validators.required],
nomProj: ["", Validators.required],
descProj: ["", Validators.required],
besProj: ["", Validators.required]
});
this.secondFormGroup = this._formBuilder.group({
pers: this._formBuilder.array([this.createItem()])
});
this.thirdFormGroup = this._formBuilder.group({
backlog: this._formBuilder.array([this.createFonct()])
});
}
createItem(): FormGroup {
return this._formBuilder.group({
name: ["", Validators.required],
poste: ["", Validators.required]
});
}
createFonct(): FormGroup {
return this._formBuilder.group({
fonctionnalite: ["", Validators.required],
userStory: ["", Validators.required]
});
}
I call my service and before I concatenate formGroup.value.
addProjet() {
this.nouveauProjet =
this.firstFormGroup.value +
this.secondFormGroup.value +
this.thirdFormGroup.value;
console.log(this.nouveauProjet);
this.ajoutProj
.addProj(this.nouveauProjet)
.toPromise()
.then(res => {
console.log(res.json);
});
}
}
In html file I call addProject function then I print {{nouveaProjet | json}} value I get this :
"[object Object][object Object][object Object]"
How to print all values ?
Update: this.firstFormGroup.value, this.secondFormGroup.value, this.thirdFormGroup.value gives in order :
Object cannot be concatenated with + so you could try:
Old way:
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
this.nouveauProjet = __assign(this.firstFormGroup.value, this.secondFormGroup.value, this.thirdFormGroup.value);
Using ES6 spread operator:
this.nouveauProjet = {
...this.firstFormGroup.value,
...this.secondFormGroup.value,
...this.thirdFormGroup.value
};

Categories