Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

form group custom validators and advice #292

Closed
acadianaapps opened this issue Feb 9, 2023 · 6 comments
Closed

form group custom validators and advice #292

acadianaapps opened this issue Feb 9, 2023 · 6 comments

Comments

@acadianaapps
Copy link
Contributor

This doesn't seem to be a direct ngx-sub-form issue, but I'm struggling with figuring out why the form isn't broadcasting a last key stroke change.

1st would the following code be the correct ngx-sub-form way to add a custom validator to the group itself?

(not sure it matters but the root form of this sub has ChangeDectectionStrategy.OnPush)

@Component({
  selector: 'maintanence-details-control',
  templateUrl: './maintanence-details.component.html',
  styleUrls: ['./maintanence-details.component.css'],
  providers: subformComponentProviders(MaintanenceDetailsControl)
})
export class MaintanenceDetailsControl {

  public form = createForm<MaintanenceDetails>(this, {
    formType: FormType.SUB,
    formControls: {
      collectiveOut: new FormControl(null, Validators.required),
      collectiveIn: new FormControl(null, Validators.required),
      collectiveHours: new FormControl(null, Validators.required),
      engineOut: new FormControl(null, Validators.required),
      engineIn: new FormControl(null, Validators.required),
      engineHours: new FormControl(null, Validators.required),
    },
  })

  constructor(
  ) {
    this.form.formGroup.addValidators([
      this.collectiveInHoursCantBeLessThanOutHours(this.form.formControlNames.collectiveOut, this.form.formControlNames.collectiveIn),
      this.engineInHoursCantBeLessThanOutHours,
    ]);
  }

  collectiveInHoursCantBeLessThanOutHours(_collectiveOut, _collectiveIn): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      const collectiveOut = control.get(_collectiveOut);
      const collectiveIn = control.get(_collectiveIn);
      const collectiveHourCalc = collectiveIn?.value - collectiveOut?.value;
      control.get(this.form.formControlNames.collectiveHours).setValue(Number(collectiveHourCalc) > 0 ? collectiveHourCalc.toFixed(2) : null, { onlySelf: true });
      return collectiveIn && collectiveOut && collectiveOut?.value > collectiveIn?.value ? { collectiveInHoursLessThanOut: true } : null;
    };
  }

  engineInHoursCantBeLessThanOutHours: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
    const engineOut = control.get('engineOut');
    const engineIn = control.get('engineIn');
    const engineHours = control.get('engineHours');
    const engineHourCalc = engineIn?.value - engineOut?.value;
    engineHours.setValue(engineHourCalc > 0 ? engineHourCalc.toFixed(2) : null, { onlySelf: true });
    return engineIn && engineOut && engineOut?.value > engineIn?.value ? { engineInHoursLessThanOut: true } : null;
  };

  // collectiveHoursCantBeMoreThanEngineHours: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
  //   const engineHours = control.get('engineHours');
  //   const collectiveHours = control.get('collectiveHours');
  //   return engineHours && collectiveHours && (engineHours?.value < collectiveHours?.value) ? { collectiveHoursMoreThanEngine: true } : null
  // };

}

The issues I'm running into with this is that the form group value itself is not showing the correct sum of hours. Now what gets me is the field in the view shows the correct value but the form group value is wrong. (see below screenshot) Notice the field view "Col Hours" is correct, but the form value itself "collectiveHours" is wrong.

image

Its not updating on the very last key stroke. If a user would add a zero after the last digit in a hour field it would update correctly. So how does one force an update, is this an ngx-sub-form issues, or do I need to try and watch form.formGroup.value for changes?

Thanks ahead of time

@maxime1992
Copy link
Contributor

Hello. Is your sub form in an invalid state while you notice this? Without checking, I think the value isn't broadcasted up if the form is invalid so that may be it

@acadianaapps
Copy link
Contributor Author

@maxime1992
I'm wondering if it may be related to #93

This image shows that subForm is valid, but the engine hours remain null.

image

But if I was to do as mention in the #93 issue and just type 0 the blur out then updates the group
image

I tried the suggestions in the previous issue but didn't help. adding the [data.issue-93] to the html template.

Also just FYI have refactored the component to this utilizing the "formGroupOptions" of "createForm":

import { Component } from '@angular/core';
import { AbstractControl, FormControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
import { createForm, FormType, subformComponentProviders } from 'ngx-sub-form';
import { MaintanenceDetails } from 'src/app/log/models/log.model';

@Component({
  selector: 'maintanence-details-control',
  templateUrl: './maintanence-details.component.html',
  styleUrls: ['./maintanence-details.component.css'],
  providers: subformComponentProviders(MaintanenceDetailsControl)
})
export class MaintanenceDetailsControl {

  collectiveInHoursCantBeLessThanOutHours(_collectiveOut, _collectiveIn): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      const collectiveOut = control.get(_collectiveOut);
      const collectiveIn = control.get(_collectiveIn);
      const collectiveHourCalc = collectiveIn?.value - collectiveOut?.value;
      control.get('collectiveHours').setValue(Number(collectiveHourCalc) > 0 ? collectiveHourCalc.toFixed(2) : null, { onlySelf: true });
      return collectiveIn && collectiveOut && collectiveOut?.value > collectiveIn?.value ? { collectiveInHoursLessThanOut: true } : null;
    };
  }

  engineInHoursCantBeLessThanOutHours: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
    const engineOut = control.get('engineOut');
    const engineIn = control.get('engineIn');
    const engineHours = control.get('engineHours');
    const engineHourCalc = engineIn?.value - engineOut?.value;
    engineHours.setValue(engineHourCalc > 0 ? engineHourCalc.toFixed(2) : null, { onlySelf: true });
    return engineIn && engineOut && engineOut?.value > engineIn?.value ? { engineInHoursLessThanOut: true } : null;
  };

  collectiveHoursCantBeMoreThanEngineHours: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
    const engineHours = control.get('engineHours');
    const collectiveHours = control.get('collectiveHours');
    return engineHours && collectiveHours && (engineHours?.value < collectiveHours?.value) ? { collectiveHoursMoreThanEngine: true } : null
  };


  public form = createForm<MaintanenceDetails>(this, {
    formType: FormType.SUB,
    formGroupOptions: {
      updateOn: 'blur',
      validators: [
        this.collectiveInHoursCantBeLessThanOutHours('collectiveOut', 'collectiveIn'),
        this.engineInHoursCantBeLessThanOutHours,
        this.collectiveHoursCantBeMoreThanEngineHours,
      ]
    },
    formControls: {
      collectiveOut: new FormControl(null, Validators.required),
      collectiveIn: new FormControl(null, Validators.required),
      collectiveHours: new FormControl(null, Validators.required),
      engineOut: new FormControl(null, Validators.required),
      engineIn: new FormControl(null, Validators.required),
      engineHours: new FormControl(null, Validators.required),
    },
  })

}

@maxime1992
Copy link
Contributor

maxime1992 commented Feb 9, 2023

Can you please try to build a minimal repro on Stackblitz. Just necessary fields etc to keep it as simple as possible

@acadianaapps
Copy link
Contributor Author

Hopefully this helps

Stackblitz Example

@maxime1992
Copy link
Contributor

Ok so thanks for the repro! That said, it's definitely not a minimal repro. A minimal repro consist of trying to create a dead simple repro with only the strict minimum to help pin point the issue by making a work upstream of removing everything that's not needed in the demo. There's load of context in yours.

I've still taken a look. I suspect, it might come from the fact that you're computing a field in the form and updating it through a validator. That really sounds like where the issue might be and it seems completely unrelated to ngx sub form.

I'd strongly encourage you to have computed values outside of the form if possible and otherwise as a first step not update the value in the validator.

I'll have to close this issue as it's not ngx sub form related and I don't have much more time to investigate. Try out the above and otherwise ask on stackoverflow I reckon.

If you do ask on stackoverflow, I'd strongly encourage you to just create one simple form, no ngx-sub-form at all, pure angular. And try to reproduce this with only the needed fields and minimal amount of code needed. If you manage to do that you'll probably get some help on SO :)

If it happens to be ngx sub form related feel free to reopen though!

@acadianaapps
Copy link
Contributor Author

@maxime1992 Thanks for your time and great library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants