ngneat / reactive-forms

(Angular Reactive) Forms with Benefits 😉
https://www.netbasal.com
611 stars 56 forks source link

feat(reactive-forms): add invalid$ and valid$ #156

Closed jafaircl closed 2 years ago

jafaircl commented 2 years ago

Add invalid$ and valid$ control status properties to controls.

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Documentation content changes
[ ] Other... Please describe:

What is the current behavior?

Currently, the valid/invalid state of the controls are not exposed as observables.

Issue Number: N/A

What is the new behavior?

This will expose the valid and invalid statuses as valid$ and invalid$ in the same way enabled and disabled are exposed as enabled$ and disabled$.

Does this PR introduce a breaking change?

[ ] Yes
[x] No

Other information

jafaircl commented 2 years ago

I actually have another branch with name and path getters as well as a clone method. If you are ok with it, I have found uses for them and I can put them in this branch.

NetanelBasal commented 2 years ago

What are the use cases?

jafaircl commented 2 years ago

For name and path, two I can come up with off the top of my head are:

1) Validation. Similar to react-hook-form's resolvers, we can validate against a schema using a variety of libraries (i.e. class-validator or yup). But, since the validation runs at the FormGroup level, we need a way to either pull errors down to the individual controls for display/logic or allow the control to get the error from the parent. If we organize errors by control name or path, we can easily do that with these 2 getters. Here is a very simplified example using class-validator as a validator for a FormGroup:

class MyClass {
  @IsString()
  name!: string;
}
const control = new FormGroup({ name: new FormControl('') });
control.controls.name.setValidators((ctrl) => {
  const classInstance = plainToClass(MyClass, control.value);
  const validationErrors = validateSync(classInstance);
  for (const error of validationErrors) {
    // a more robust example would use the `children` property of the error along with `path`
    if (error.property === (ctrl as FormControl<any>).name) {
      return { classValdiatorErrors: error.constraints };
    }
  }
  return null;
});
  1. Anytime you need to get the name or path in the template. Say you have a complicated input that needs to perform some logic on which fields to show based on the formControlName. Or you have an onchange function that performs logic depending on the path of the control.

The path is also formatted to be directly useable by control.get(path).

For cloning the controls, anytime you need a copy of the control. You could need to copy data and immediately start editing it such as with copying an old record to add a new one. Or if you wanted to implement large form edits as drafts first.

I can also do these as separate PRs. Whatever is best/easiest. I only mentioned adding them to this PR to lower your workload.

NetanelBasal commented 2 years ago

But you're not getting the same reference when you modify a FormGroup.