-
Notifications
You must be signed in to change notification settings - Fork 33
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
feat(lib): formGroup validator oneOf
#94
base: master
Are you sure you want to change the base?
Conversation
This closes #91
@@ -66,6 +74,63 @@ export abstract class NgxSubFormComponent<ControlInterface, FormInterface = Cont | |||
|
|||
private controlKeys: (keyof FormInterface)[] = []; | |||
|
|||
// instead of having the validators defined in the utils file | |||
// we define them here to have access to the form types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that really just means we should extract the types to their own file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point was to get access to the form types without having to specify them when calling the validator but I hear your point... (CF next answer)
|
||
// `ngxSubFormValidators` should be used at a group level validation | ||
// with the `getFormGroupControlOptions` hook | ||
protected ngxSubFormValidators = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I understand this pattern is following the angular pattern of namespace.validatorname
, I think that concept by angular is actually a bad idea, because it is unfriendly to tree shakers - if you use one validator, youre basically guaranteed to have to import them all. I think a simple pure function per validator, in it's own file is the most tree shaking friendly option
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started exactly how you described it.
But then realized we'd have to pass the types as argument =/
Not impossible... not really awful but slightly more heavy 🤷♂️?
Although, I do have to admit that it feels wrong to have validators defined within the class and require the class to be instantiated before being able to use them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making another pass on this one, do we really care about tree shaking here? I wouldn't expect ngx-sub-form to embed plenty of validators (if so they should really be extracted into a separated package IMO). Plus, I'm not sure that using a namespace is better for tree shaking 🤔 but yes could have functions where we need to pass the type to.
|
||
return { | ||
oneOf: oneOfErrors, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing some complex requirement here, but this feels like it could be simplified to the following (no idea if this compiles ;) )
const notNullKeys: Array<keyof FormInterface> = keysArray.filter(() => {
const control: AbstractControl | null = formGroup.get(key as string);
if (!control) {
throw new OneOfValidatorUnknownFieldError(key as string);
}
return !isNullOrUndefined(control.value);
});
if (notNullKeys === 1) {
return null;
}
return { oneOf: notNullKeys };
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That code doesn't handle if multiple values are defined. If there's more than 1 value, which one to pick? That should just be an error IMO
// `ngxSubFormValidators` should be used at a group level validation | ||
// with the `getFormGroupControlOptions` hook | ||
protected ngxSubFormValidators = { | ||
oneOf(keysArray: (keyof FormInterface)[][]): TypedValidatorFn<FormInterface> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this an array of arrays?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because one form component may have multiple properties that are polymorphic.
Example:
interface User {
id: string;
vehicle: MotorBike | Car | Truck;
animal: Cat | Dog;
}
In the case we want to handle them without breaking down into sub components we can do:
oneOf([
['motorBikeVehicle', 'CarVehicle', 'TruckVehicle'],
['CatVehicle', 'DogVehicle']
])
@maxime1992 I think I've understood now what you were working to achieve but I realised that my thinking was too complex to explain in a MR comment, so instead I wrote it down as a commit :) See #95 I'm not saying this is what we should do, just noting down an alternative idea for discussion. If we do decide my proposal is a better strategy I'll be happy to finish it up properly |
|
||
export class OneOfValidatorUnknownFieldError extends Error { | ||
constructor(field: string) { | ||
super(`"oneOf" validator requires to keys from the FormInterface and "${field}" is not`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
@zakhenry I re-read my MR and yours, I still think that managing the case where you have multiple Let me know what you think :) |
This closes #91