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

Feature Request: Support Generation from Domain Models #173

Open
etegan opened this issue Dec 16, 2024 · 9 comments
Open

Feature Request: Support Generation from Domain Models #173

etegan opened this issue Dec 16, 2024 · 9 comments

Comments

@etegan
Copy link

etegan commented Dec 16, 2024

Hello and first of: Thanks for these great packages!

Problem Statement

Currently, the generator requires models with nullable fields to represent form state, forcing developers to maintain two parallel models:

  1. A domain model with proper nullability representing the business logic
  2. A form state model with nullable fields for form handling

This leads to unnecessary duplication and makes it harder to maintain type safety between form submissions and domain objects.

Example of current approach:

// Domain Model
class User {
  final String name;      // Required in domain
  final String email;     // Required in domain
  final String? phoneNumber;
}

// Currently needs manual form model
class UserFormModel {
  final String? name;     // Must be nullable for form
  final String? email;    // Must be nullable for form
  final String? phoneNumber;
}

Proposed Enhancement

The generator should automatically generate both:

  1. The form model class with appropriate nullable fields
  2. An extension method on the domain model for conversion

The @Rf annotation should support configuring partial data injection through related models.

Example Usage

// Domain model with partial data configuration
@Rf(partialData: {
  'UserInvitation': {
    'email': 'email'  // maps invitation.email to userFormModel.email
  }
})
class User {
  final String name;
  final String email;
  final String? phoneNumber;
}

// Generated form model with inferred validators
class UserFormModel {
  final String? name;     // Generated with RequiredValidator() since non-nullable in User
  final String? email;    // Generated with RequiredValidator() since non-nullable in User
  final String? phoneNumber;  // No RequiredValidator() since nullable in User
}

// Generated extension
extension UserFormExtension on User {
  UserFormModel get formModel => UserFormModel(
    name: name,
    email: email,
    phoneNumber: phoneNumber,
  );
}

// Usage in UI
UserFormBuilder(
  // Optional initial data from domain model using generated extension
  model: existingUser?.formModel,  
  // Partial data injection configured by @Rf annotation
  userInvitation: UserInvitation(email: "example@email.com"),
  builder: (context, form, child) => ...
)

Benefits

  • Reduces boilerplate and maintenance overhead
  • Maintains single source of truth for model structure
  • Preserves type safety throughout the form lifecycle
  • Enables better IDE support and static analysis
  • Allows seamless integration with domain-driven design practices
  • Provides clean, intuitive API through generated extensions on domain model class
  • Declarative configuration of partial data injection
  • Automatic validation rules based on domain model nullability

Implementation Notes

The generator would need to:

  1. Analyze domain model field nullability
  2. Parse @Rf annotation for partial data configuration
  3. Generate form state model with nullable fields
  4. Generate validation rules based on domain model nullability
  5. Generate extension methods for converting domain models to form models
  6. Generate partial data injection logic based on annotation configuration

Would you consider this enhancement for a future release? It would significantly improve the developer experience when working with domain-driven architectures.

@vasilich6107
Copy link
Contributor

@vasilich6107
Copy link
Contributor

It generates output based on the RequiredValidator and generates ...Output form with proper nullability items

Here is an example
https://github.com/artflutter/reactive_forms_generator/blob/bets/packages/reactive_forms_generator/example/lib/docs/login/login_output.dart

For now not every aspect is working but you can try

@vasilich6107
Copy link
Contributor

The solution with JSON

@Rf(partialData: {
  'UserInvitation': {
    'email': 'email'  // maps invitation.email to userFormModel.email
  }
})

is not scalable cause it falls apart in complex cases due to the lack of static analysys
The main idea of generator to get a statically reliable result as much as possible

@etegan
Copy link
Author

etegan commented Dec 17, 2024

Fantastic! Thanks for the quick reply. I realised that a lot of the functionality I was looking for was also available by writing named constructors on the model 😊

@etegan etegan closed this as completed Dec 17, 2024
Copy link

Hi @etegan!
Your issue has been closed. If we were helpful don't forget to star the repo.

Please check our reactive_forms_widget package

We would appreciate sponsorship subscription or one time donation
https://github.com/sponsors/artflutter

@etegan etegan reopened this Dec 18, 2024
@etegan
Copy link
Author

etegan commented Dec 18, 2024

Hello again. I noticed what I think might be a bug. The named constructor approach that I mentioned together with the @RfArray annotations results in a UserForm without the add* methods

class User {
  final String name;
  final List<String> roles;

  // Without this constructor the addRolesItem is generated
  const User.withDefaults({
    this.name = '',
    this.roles = const [], // Problem: @RfArray won't generate addRolesItem
  });

  const User({
    @RfControl(validators: [RequiredValidator()])
    required this.name,
    @RfArray(validators: [RequiredValidator()])
    required this.roles,
  });

}

@etegan
Copy link
Author

etegan commented Dec 18, 2024

Solved: the named constructor can not be the first constructor. This works:

class User {
  final String name;
  final List<String> roles;

  const User({
    @RfControl(validators: [RequiredValidator()])
    required this.name,
    @RfArray(validators: [RequiredValidator()])
    required this.roles,
  });
  
  // Named constructor comes after default constructor
  const User.withDefaults({
    this.name = '',
    this.roles = const [],
  });

}

@etegan etegan closed this as completed Dec 18, 2024
Copy link

Hi @etegan!
Your issue has been closed. If we were helpful don't forget to star the repo.

Please check our reactive_forms_widget package

We would appreciate sponsorship subscription or one time donation
https://github.com/sponsors/artflutter

@etegan
Copy link
Author

etegan commented Dec 18, 2024

Leaving it open if you still want to classify it as a bug :)

@etegan etegan reopened this Dec 18, 2024
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

2 participants