0

I have a formControl field that I created for date of birth (dob). My goal is to simply have my directive format my date field entry into MM/DD/YYYY when they enter a date using MMDDYYYY, which the directive does fine. I also have a validator on the dob field (using reactive form method), which is using a regular expression to validate that the entry is even a date.

The validator is marking that the form as having an error. When debugging, I've found that the directive is correctly updating the value of the input form, but upon continuing debugging the remaining code (built-in Angular) it appears that the remaining code (which appears to be the code that handles validation) still sees the old value (example 02022010). I am new to Angular and Javascript, so my only thought is maybe it is a scoping issue?

I've tried using only the validator without the directive at all to make sure that the pattern was registering correctly. The pattern is indeed validating the date field right, so I don't believe the Validator.pattern to be the issue here in the sense of a bad pattern.

The following post: Validate md-datepicker using formcontrol is closely related to mine but it didn't help me, nor am I using a datepicker or want a datepicker.

app.component.ts

import { Component } from '@angular/core';
import { FormsModule, ReactiveFormsModule, FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
  <form [formGroup]='searchForm'>
  <input #firstNameField formControlName='firstName' placeholder="Enter first name">
  <input #lastNameField formControlName='lastName' placeholder="Enter last name">
  <input #dobField formControlName='dob' placeholder='MM/DD/YYYY' autocomplete="off" appDOBinput></form>
  <div *ngIf="searchForm.controls['dob'].hasError('pattern')">Date is invalid</div>
  `,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  searchForm: FormGroup;
  // tslint:disable-next-line: max-line-length
  REGEX = RegExp('^(?:(?:10|12|0?[13578])/(?:3[01]|[12][0-9]|0?[1-9])/(?:1[8-9]\\d{2}|[2-9]\\d{3})|(?:11|0?[469])/(?:30|[12][0-9]|0?[1-9])/(?:1[8-9]\\d{2}|[2-9]\\d{3})|0?2/(?:2[0-8]|1[0-9]|0?[1-9])/(?:1[8-9]\\d{2}|[2-9]\\d{3})|0?2/29/[2468][048]00|0?2/29/[3579][26]00|0?2/29/[1][89][0][48]|0?2/29/[2-9][0-9][0][48]|0?2/29/1[89][2468][048]|0?2/29/[2-9][0-9][2468][048]|0?2/29/1[89][13579][26]|0?2/29/[2-9][0-9][13579][26])$');

  constructor() {
    this.searchForm = new FormGroup({
      firstName: new FormControl(''),
      lastName: new FormControl(''),
      dob: new FormControl('', { validators: Validators.pattern(this.REGEX), updateOn: 'blur' })
    });
  }
}

date-input.directive.ts

import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
  selector: '[formControlName] [appDOBinput]',
})
export class DateInputDirective {

  constructor(public ngControl: NgControl) { }

  @HostListener('ngModelChange', ['$event'])
  onModelChange(event) {
    this.onInputChange(event, false);
  }

  @HostListener('keydown.backspace', ['$event'])
  keydownBackspace(event) {
    this.onInputChange(event.target.value, true);
  }

  onInputChange(event, backspace) {
    let newVal = event.replace(/\D/g, '');
    if (backspace && newVal.length <= 6) {
      newVal = newVal.substring(0, newVal.length - 1);
    }
    if (newVal.length === 0) {
      newVal = '';
    } else if (newVal.length <= 3) {
      newVal = newVal.replace(/^(\d{0,2})/, '$1/');
    } else if (newVal.length <= 4) {
      newVal = newVal.replace(/^(\d{0,2})(\d{0,2})/, '$1/$2/');
    } else {
      newVal = newVal.replace(/^(\d{0,2})(\d{0,2})(\d{0,4})/, '$1/$2/$3');
    }
    console.log('value in directive: ' + newVal);
    this.ngControl.valueAccessor.writeValue(newVal);
  }
}

The console output from the directive is showing the correct output such as input 02022010 would output 02/02/2010. However, if I continue to debug step by step eventually (within a file like core.js) I'll see the value field as having 02022010 still. Once all the logic is complete, the output after the date field is 'Date is invalid'

It is almost like the logic involved with validation is still using the old value.

user2278842
  • 65
  • 1
  • 7
  • Could the updateon blur be the problem? Why aren't updating on change? – Juan Aug 03 '19 at 17:45
  • @juan - I probably could update on change but then the directive is just hit on each keystroke. – user2278842 Aug 03 '19 at 17:49
  • Could it be that the value you enter is not updated by the directive because the change hasn't been called? – Juan Aug 03 '19 at 17:53
  • @Juan - I think that is what is happening. I think the directive is correctly updating the value on the input field as you can see upon entering 02022019 that it quickly changes it to 02/02/2019. It is likely the other portion of the code has not been updated with that. – user2278842 Aug 03 '19 at 18:01
  • Change update on to change in the field creation and see uf it works – Juan Aug 03 '19 at 18:05
  • @Juan - It doesn't work. Still reports that date is invalid. – user2278842 Aug 03 '19 at 18:14
  • I think the FormContro is not getting notified of the change being done by the directive. Not sure if this would be the right solution but you can try emiting an event when the directive changes de input value and then bind it to a function in the component that updates the FormControlValue. Something like this: https://stackoverflow.com/questions/37962701/emit-event-from-directive-to-parent-element – Juan Aug 04 '19 at 13:05

2 Answers2

0

I think problem is in your Regex to validate date. Try this

/^((0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.](19|20)?[0-9]{2})*$

Chaitanya
  • 847
  • 1
  • 8
  • 15
  • @chaitanya - I wasn't aware this could be done this way; however, it doesn't seem to work. I'm wondering if it has to do with the fact that I'm using date as a string here and not a date object? – user2278842 Aug 03 '19 at 17:55
  • For date are you using input type date or text or any datepicker? – Chaitanya Aug 03 '19 at 18:18
  • @chaitanya - The input type is just a string. No datepicker because the app is used in a fast pace environment and can't have users clicking back decades in a datepicker to find someones birth date. This is why I posted that a user should be able to enter MMDDYYYY and it'll be converted to MM/DD/YYYY by the directive. I've found that the datepicker seems to have its own logic that interferes with mine too. – user2278842 Aug 03 '19 at 18:24
  • It means user will enter only MMDDYYY. Then my answer won't work. Let me try other way and will update my answer. – Chaitanya Aug 03 '19 at 18:29
  • If validation is happening onBlur and there is no problem in your directive. It means there is problem in Regex. I updated my answer once check – Chaitanya Aug 03 '19 at 19:13
  • @chaitanya - I tried yours and it still reports invalid date. I tried removing the directive with my regex and it validates fine. When I did the same with the regex you provided, it says it is invalid. I think my regex is fine here. – user2278842 Aug 03 '19 at 23:53
0

For validating date you can use date validator of @rxweb/reactive-form-validators without making any custom directive or function, it will allow you to validate date as per your format

 ngOnInit() {
        this.searchForm = this.formBuilder.group({
            birthDate:['', RxwebValidators.date()], 
        });
    }

You just need to import RxReactiveFormsModule in app module

Here is the complete component code :

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from "@angular/forms"
import { RxwebValidators } from '@rxweb/reactive-form-validators';

@Component({
    selector: 'app-date-add-validator',
    templateUrl: './date-add.component.html'
})
export class DateAddValidatorComponent implements OnInit {
    searchForm: FormGroup

 constructor(
        private formBuilder: FormBuilder )
 { }

    ngOnInit() {
        this.searchForm = this.formBuilder.group({
            birthDate:['', RxwebValidators.date()], 
        });
    }
}
<div>
  <form  [formGroup]="searchForm">
    <div class="form-group">
      <label>Birth Date</label>
      <input type="text" formControlName="birthDate" class="form-control"  />
      <small class="form-text text-muted">You must enter a valid date only<br/></small>
     <small class="form-text text-danger" *ngIf="searchForm.controls.birthDate.errors">{{searchForm.controls.birthDate.errors.date.message}}<br/></small> 
    </div>
    <button [disabled]="!searchForm.valid" class="btn btn-primary">Submit</button>
  </form>
</div>

Working Example

Ushmi Dave
  • 276
  • 1
  • 7