Search code examples
angularvalidation

Angular restore ngModel input field to it's previous value


I'm using angular 10 with nebular 6.

What I want to achieve:

I have a form where are two input date fields, start of project and end of project. Whether the user wants to enter an invalid date (with nb-datepicker), let's say start of project is after end of project the form have to forbid it and immediately set the changed field value back to the previous.

What I already tried with no luck:

  • Placed(ngModelChange)="validateDate($event)" before [(ngModel)]="startOfProject". It created unpredictable behavior.
  • Removed [(ngModel)] alltogether and added (change)="validateDate($event) and kept the old value. Later I found out that this is a bad coding practice and not maintainable.

In AngularJS there was $scope.$watch where one can easily obtain the previous value. What is the Angular 10 way of doing this?


Solution

  • Which datapicker you are using doesn't make any difference, since the functionality you are searching for is based on RxJs and angular ReactiveFormsModule. You can use formGroup.valueChanges with RxJs pairwise

    HTML

    <form [formGroup]="dateForm">
        <mat-form-field style="margin-right: 10px;">
            <input matInput [matDatepicker]="picker" formControlName="startdate" placeholder="Start date"/>
            <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
            <mat-datepicker #picker></mat-datepicker>
        </mat-form-field>
        <mat-form-field>
            <input matInput [matDatepicker]="picker2" formControlName="enddate" placeholder="End date"/>
            <mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
            <mat-datepicker #picker2></mat-datepicker>
        </mat-form-field>
    </form>
    

    TS

    dateForm: FormGroup;
    constructor(private formBuilder: FormBuilder) {
        this.dateForm = this.formBuilder.group({
            startdate: [null, Validators.required],
            enddate: [null, Validators.required],
        });
    }
    
    ngOnInit(): void {
        this.dateForm.valueChanges.pipe(
            startWith({ oldValue: null, newValue: null }),
            distinctUntilChanged(),
            pairwise(),
            map(([oldValue, newValue]) => { return { oldValue, newValue } })
        )
        .subscribe({
            next: (val) => {
                console.log(val)
                // implement condition and set controls accordingly using
                // this.dateForm.controls.startdate.setValue(whatever date)
                // this.dateForm.controls.enddate.setValue(whatever date)
            }
        });
    }
    

    Here is the stackblitz

    Have in mind that this is just basic example explaining how you can do that, but its not complete, to begin with you should add some types here. To formControls, to rxjs. You should destroy subscription when no longer needed, etc...

    Additionally, I just took whatever project and implemented staff, although this particular example will be practically identical in the latest versions of angular, stackblitz example uses old version of angular, so this is not moduleless, you will be able to optimize some staff if you use angular 16-17