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

feat(lib): formGroup validator oneOf #94

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

maxime1992
Copy link
Contributor

This closes #91

@maxime1992 maxime1992 added scope: lib Anything related to the library itself type: feature This is a new feature labels Aug 29, 2019
@maxime1992 maxime1992 requested a review from zakhenry August 29, 2019 05:59
@maxime1992 maxime1992 self-assigned this Aug 29, 2019
@@ -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
Copy link
Contributor

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.

Copy link
Contributor Author

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 = {
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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,
};
Copy link
Contributor

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 };

Copy link
Contributor Author

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> {
Copy link
Contributor

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?

Copy link
Contributor Author

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']
])

@zakhenry
Copy link
Contributor

@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`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

@maxime1992
Copy link
Contributor Author

@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

@zakhenry I re-read my MR and yours, I still think that managing the case where you have multiple oneOf on the same form is a good idea and could come up for ex a Person which has cat|dog and flat | house and car | motorBike. In our case at cloudnc a ToolAssembly which has a OneTool and a OneHolder.

Let me know what you think :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope: lib Anything related to the library itself type: feature This is a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

oneOf validator for polymorphic data
2 participants