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

@FormArrayAnnotation() with inheritance #99

Open
Guillaume-Cornet opened this issue Apr 11, 2023 · 17 comments
Open

@FormArrayAnnotation() with inheritance #99

Guillaume-Cornet opened this issue Apr 11, 2023 · 17 comments

Comments

@Guillaume-Cornet
Copy link

Hi,

I have an equivalent of this case in my model.

@ReactiveFormAnnotation()
class Day {
  Day({
    @FormArrayAnnotation() required this.events,
  });

  List<Event> events;
}

@FormGroupAnnotation()
abstract class Event {
  Event({
    @FormControlAnnotation() required this.id,
  });
}

@FormGroupAnnotation()
class PostOp extends Event {
  PostOp({
    required super.id,
  })
}

@FormGroupAnnotation()
class PreOp extends Event {
  PreOp({
    required super.id,
    this.date,
  })

  DateTime date;
}

But i can't make it working with the forms and i don't find in documentation how to use @FormArrayAnnotation with inheritance.

Is this use case possible with reactive_forms ?

ps : apart from this problem, this lib is awesome ! In this project i have a big forms with ~250 different fields to update with many validations and i looking for the more elegant way to do that.

Thanks, Guillaume.

@vasilich6107
Copy link
Contributor

Hi.
Please create a demo repo with you flow and I will try to help you.

If you are interested in an example with huge amount of fields you can create one as PR to this repo and I will try to help you organising your approach

@Guillaume-Cornet
Copy link
Author

Hi,

Here https://github.com/Guillaume-Cornet/reactiveForm-inheritance-poc

This is a repo with an example of the architecture from my project.

You'll find a 'model' folder with the structure :

  • An articulation may have between 0 and infinity abstract events
  • An event is exetended by one of this element: HipSurgery, KneeSurgery, PreOp, PostOp, Undesirable

Each on this event has a custom dynamic form

Thanks, Guillaume.

@vasilich6107
Copy link
Contributor

Please supply all required folder for running
When I try flutter create .

 flutter create --platforms=android .  
"reactiveForm-inheritance-poc" is not a valid Dart package name.

I do not have time to fight with running the repo

@vasilich6107
Copy link
Contributor

flutter create --project-name=pocreactiveforms . did the trick

@vasilich6107
Copy link
Contributor

first question
why do you need this complex structure for events?

events: [
          PreOp(type: EventType.preOp, id: 1),
          KneeSurgery(type: EventType.kneeSurgery, id: 2),
          HipSurgery(type: EventType.hipSurgery, id: 3),
          PostOp(type: EventType.postOp, id: 4),
          Undesirable(type: EventType.undesirable, id: 5),
        ]

Can you place each event in separate array?

@Guillaume-Cornet
Copy link
Author

Hi,

Sorry, i have generate the project with IntelliJ and i did'nt get warning for the name.
And have run this poc only on WEB.

first question why do you need this complex structure for events?

events: [
          PreOp(type: EventType.preOp, id: 1),
          KneeSurgery(type: EventType.kneeSurgery, id: 2),
          HipSurgery(type: EventType.hipSurgery, id: 3),
          PostOp(type: EventType.postOp, id: 4),
          Undesirable(type: EventType.undesirable, id: 5),
        ]

Can you place each event in separate array?

This structure come from an old apps and represent a life cycle of patient surgery.
Example: Patient need surgery so :

  • he have a Pre operatory exam
  • then he have a knee surgery
  • then he have a post operatory exam
  • then he have a problem with a surgery, so a Undesirable event is created
  • then he have a a knee surgery again.

In the complete model, all this event has a date field and will be sort by date.
For this domain, i think it's more logical to have this events in one array.

@vasilich6107
Copy link
Contributor

I have some ideas about implementation.
It requires some time to add changes to the package + implement the proper example.

@BenjiFarquhar
Copy link
Contributor

BenjiFarquhar commented Dec 15, 2023

@vasilich6107 I would like to have inheritance in my form models, too. Two ways I currently cast to dynamic are:

(this.formModel as dynamic).nameControl!

And making the return type of this function dynamic when it should be Details (a base class model data type):

  dynamic detailsModel({VgnItm? fromItm}) {
    final itm = fromItm ?? currentItm?.vgnItm;

    return Details(
      name: itm?.name,
      companyName: itm?.companyName,
      pricePoint: itm?.pricePoint,
      description: itm?.description,
    );
  }

Details above is the model, E.G., theT in FormModel<T>

I use dynamic because several subclasses re-use this code. So, if we could get the FormModel to have an inheritance hierarchy so that subclasses of DetailsFormModel have those controls, E.G., formModel.nameControl and they also have a model that is a subclass of Details, it would improve code quality.

@vasilich6107
Copy link
Contributor

vasilich6107 commented Dec 15, 2023

Hi @BenjiFarquhar
I recently did the same feature as @Guillaume-Cornet was trying to implement.
Everything went smooth.

I created class A and subclassed all items from it

class BA extends A {}

then I was able to easily manipulate them.
The only trick was that I manipulated them as FormArray and not FormGroupArray

Could you provide the real world example cause I do not understand the issue.

@BenjiFarquhar
Copy link
Contributor

BenjiFarquhar commented Dec 15, 2023

@vasilich6107 I can't really reconcile how I would use a FormArray.

This is what I want:

@ReactiveFormAnnotation()
class Details<TVgnItm extends VgnItm> extends Model<TVgnItm> {
  String? description;
  String? companyName;
  String? name;
  PricePoint? pricePoint;

  Details({
    @FormControlAnnotation(validators: [VpValidators.maxLengthXXL])
    this.description,
    @FormControlAnnotation(
      validators: [RequiredValidator()],
    )
    this.companyName,
    @FormControlAnnotation(
      validators: [VpValidators.required],
    )
    this.name,
    @FormControlAnnotation() this.pricePoint,
  });

  @override
  bool get isEmpty =>
      FieldUtils.isEmpty(name) &&
      FieldUtils.isEmpty(description) &&
      FieldUtils.isEmpty(pricePoint) &&
      FieldUtils.isEmpty(companyName);

  @override
  TVgnItm toEntity(TVgnItm entity) => entity.copyWith(
        name: name,
        companyName: companyName,
        description: description,
        pricePoint: pricePoint,
      ) as TVgnItm;
}


@ReactiveFormAnnotation()
class RecipeItmDetails extends Details<RecipeItm> {
  int? servesCount;
  Difficulty? difficulty;
  Duration? prepTime;
  Duration? cookTime;

  RecipeItmDetails({
    @FormControlAnnotation(
      validators: [VpValidators.maxLengthXXL],
    )
    this.servesCount = 2,
    @FormControlAnnotation(
      validators: [VpValidators.required],
    )
    this.difficulty,
    @FormControlAnnotation(
      validators: [AtLeastTenSecondsValidator()],
    )
    this.prepTime,
    @FormControlAnnotation(
      validators: [AtLeastTenSecondsValidator()],
    )
    this.cookTime,
    @FormControlAnnotation(validators: [VpValidators.maxLengthXXL])
    super.description,
    @FormControlAnnotation(
      validators: [RequiredValidator()],
    )
   super.companyName,
    @FormControlAnnotation(
      validators: [VpValidators.required],
    )
    super.name,
    @FormControlAnnotation() super.pricePoint,
  });

  @override
  bool get isEmpty {
    return FieldUtils.isEmpty(prepTime) &&
        FieldUtils.isEmpty(cookTime) &&
        FieldUtils.isEmpty(difficulty) &&
        FieldUtils.isEmpty(name) &&
        FieldUtils.isEmpty(description) &&
        FieldUtils.isEmpty(pricePoint) &&
        FieldUtils.isEmpty(companyName);
  }

  @override
  RecipeItm toEntity(RecipeItm entity) {
    return entity.copyWith(
      name: name,
      companyName: companyName,
      description: description,
      pricePoint: pricePoint,
      servesCount: servesCount,
      difficulty: difficulty,
      prepTime: prepTime,
      cookTime: cookTime,
    );
  }
}

I want RecipeItmDetails to be a Details and RecipeItmDetailsForm to be a DetailsForm so RecipeItmDetailsForm has a nameControl. Is there some workaround with FormArray?

@vasilich6107
Copy link
Contributor

Sorry
I have very few time. I do not clear understand the idea from this small pieces of code.
Could you provide a minimal runnable example with explanation what you want and what you cant acheive?

@BenjiFarquhar
Copy link
Contributor

@vasilich6107 It won't compile, as reactive_forms_generator doesn't support FormModel inheritance.

To really simplify what I want, and get to the main point:

I just want the generated RecipeItmDetailsForm to extend/implement the generated DetailsForm.

@BenjiFarquhar
Copy link
Contributor

The generated model is also Details<T> not Details<TVgnItm extends VgnItm>

I can rename TVgnItm to T, but it would be nice if the extends VgnItm carried through to the generated file.

I don't know if this is possible, I'm just asking.

@vasilich6107
Copy link
Contributor

The generator was designed to generate form representation from model. I never tested or thought that it makes sense to extend the model from another generated DetailsForm.

this is why I’m asking about the task you want to solve. Sometimes the solution is reasonable. Sometimes not.

I can’t tell which case is yours cause I do not know what you want to achieve

@BenjiFarquhar
Copy link
Contributor

It is just common to use polymorphism in programming. I'm using it to implement the abstract factory pattern. I have a base factory, a RecipeFactory, a RestaurantFactory, etc, and they each have a variation of a DetailsForm - either a BaseDetailsForm, a RecipeDetailsForm, or a RestaurantDetailsForm. I've worked on two apps that have implemented the same category of form for multiple different entities, which all need to vary independently, and we have used the abstract factory pattern both times. It is primarily useful when the user can press a button and the other entity form gets displayed.

I find code generators rarely support inheritance; it's usually a limitation. I have the same experience with Freezed; I have worked around it with dynamic, which is still better than writing lots of boilerplate.

@vasilich6107
Copy link
Contributor

I check the thread on freezed it seems like this library will also never support inheritance

@BenjiFarquhar
Copy link
Contributor

BenjiFarquhar commented Dec 30, 2023

@vasilich6107 rrousselGit/freezed#907

It could also support inheritance on non-freezed classes until freezed supports it.

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

3 participants