artflutter / reactive_forms_generator

Other
89 stars 22 forks source link

@FormArrayAnnotation() with inheritance #99

Open Guillaume-Cornet opened 1 year ago

Guillaume-Cornet commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 :

vasilich6107 commented 1 year ago

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 commented 1 year ago

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

vasilich6107 commented 1 year ago

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 commented 1 year ago

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 :

vasilich6107 commented 1 year ago

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

BenjiFarquhar commented 10 months ago

@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 commented 10 months ago

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 commented 10 months ago

@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 commented 10 months ago

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 commented 10 months ago

@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 commented 10 months ago

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 commented 10 months ago

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 commented 10 months ago

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 commented 10 months ago

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

BenjiFarquhar commented 10 months ago

@vasilich6107 https://github.com/rrousselGit/freezed/issues/907

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