Giter Site home page Giter Site logo

reactive_forms_generator's Introduction

Правила сообщества ArtFlutter

Миссия

Сердце ArtFlutter - это люди. Мы ставим интересы людей на первое место и делаем все возможное, чтобы признавать, ценить и уважать всех наших участников по всему миру. Мы приветствуем вклад всех, кто разделяет наши цели и хочет способствовать здоровому и конструктивному подходу в нашем сообществе. В связи с этим, мы приняли эти правила поведения и требуем от всех участников, согласиться и придерживаться этого руководства, чтобы поддерживать атмосферу здорового общения в сообществе.

Не оскорбляйте других людей и не судите людей по их вопросам

Подробнее...

Примеры:

 - child: Visibility для чего этот виджет?
 - Ну судя по названию для рассчета факториала
 
 - Всем привет, а есть где-то примеры как проходит собеседоваение на джуна? Что должен уметь и знать джун?
 - Да, довольно много на просторах интернета.

Если вам кажется что этот вопрос слишком прост, что его можно быстро нагуглить и вообще вопросам такого уровня тут не место - вы не правы.

Начать можно хотя бы с того что практически любой вопрос можно нагуглить и вас с вашим вопросом в следующий раз могут по просить пройти туда же.

А закончить тем что спрашивают не лично "ваше высочество", можно спокойно этот вопрос проигнорировать - на него даст ответ кто-то другой.

Помните что таким поведением после себя вы оставляете таких же токсичных "учителей".

Простое проверочное правило - если в ответ на вопрос вам хочется написать что-то отличное от ответа или уточняющего вопроса - просто воздержитесь.

Не пересекайте линию между иронией и оскорблениями

Помните - участник сообщества по ту сторону не ваш закадычный друг. Он не знает вас, не знает ваш темперамент, не видит вашей мимики и не слышит интонаций. То что в живом общении может показаться невинной шуткой в переписке может быть прочитано и понятно совсем иначе.

Если вы не чувствуете границы - воздержитесь от иронии вовсе

Уважайте чужие точки зрения, даже если они кардинально не совпадают с Вашими

Подробнее...

Противополжоные точки зрения нужны и важны, они позволяют нам взглянуть на проблему под другим углом. Вполне возможно что они сформировались под влиянием не очевидных факторов.

Уважайте их. Ведите вежливую дискуссию. Черпайте информацию и вдохновение - делитесь своим мнением.

Избегайте флуда и разговоров обо всем - используйте личные сообщения

Подробнее...

Мы всячески поддерживаем комуникацию и дружеские отношения. Но есть невидимая грань между тематическим общением и разговоров обо всем. Старайтесь избегать трансляции мемасиков, тиктоков и прочего фонового шума. В купе с реакицей на фоновый шум - чат превращается в поток сознания. Ставится невозможно отфильтровать интересные обсуждения среди флуда ровно как и задать вопрос. Вопрос коллеги просто теряется в этом потоке.

Хочется поделиться мемчинским - есть личные сообщения.

Видите что разговор перетекает на отвлеченные темы - перейдите в личные сообщения.

Хотите помочь человеку но разговор разрастается большим кол-вом уточняющих вопросов и скринов кода - перейдите в личные сообщения.

У нас не банят

Подробнее...

Мы верим в то что каждый способен осознать свою ошибку и изменить свое поведение. В случае нарушений данных правил нарушителю будет ограничена возможность писать сообщения на срок от одного дня до одного месяца. Этого вполне достаточно для вдумчивого чтения правил. В случае систематического нарушения правил вы не оставляете другого варианта как ограничить возможность написания сообщений навсегда.

Эти указания направлены на поддержку сообщества, в котором все люди должны чувствовать себя в безопасности, и иметь возможность внедрять новые идеи и вдохновлять других, в независимости от:

  • происхождения
  • семейного статуса
  • пола
  • гендерной принадлежности или формы самоидентификации
  • сексуальной ориентации
  • родного языка
  • возраста
  • физических возможностей
  • расы и этнической принадлежности
  • национального происхождения
  • социального статуса
  • религии
  • географического положения

Открытость, сотрудничество и участие - ключевые аспекты нашей работы. Мы становимся сильнее, благодаря разнообразию и активно привлекаем к участию тех, кто его поддерживает. Это руководство создано для того, чтобы дать возможность различным людям и группам взаимовыгодно взаимодействовать и сотрудничать. В этом документе описывается как ожидаемое, так и запрещенное поведение.

Ожидаемое поведение

Ожидается следующее поведение от всех участников проекта ArtFlutter:

Будьте вежливы

Цените идеи, стили и точки зрения друг друга. Мы не всегда можем согласиться, но несогласие не является оправданием плохих манер. Будьте открыты к разным точкам зрения и к возможности ошибаться. Оставайтесь добрыми и обходительными во всех взаимодействиях и коммуникациях, особенно при обсуждении достоинств и недостатков различных сторон.

Помните о своем воздействии и о том, как ваши слова и поступки могут затрагивать других людей. Будьте прямолинейными, конструктивными и уверенными в своих действиях. Возьмите на себя ответственность за свое влияние на других и свои ошибки, и если кто-то скажет, что они пострадали от ваших слов или действий, внимательно выслушайте, искренне принесите свои извинения и исправьте поведение в будущем.

Будьте прямолинейны, оставаясь профессионалом

Нам, вероятно, следует обсудить, когда критика уважительна, а когда нет. Важно говорить прямо, когда мы с чем-то не согласны или же когда думаем, что что-то можно сделать лучше. Любой человек не хочет отказываться от правды и фактов, которых придерживается. Поэтому вежливо дискутировать иногда сложно, особенно если другие, как нам кажется, не слушают. Даже когда один из собеседников может выслушать точку зрения другого, все еще сложно слышать неуважительные комментарии. Нам нужно быть честным и откровенными, но при этом проявлять уважение к собеседнику.

Будьте всесторонни

Ищите различные точки зрения. Разнообразие взглядов и людей в командах приносит что-то новое, даже если это доставляет неудобства при обсуждении. Поддерживайте все мнения. Помогайте людям с другими точками зрения слушать и быть услышанными. Особенно важно уметь отступить если ваше мнение приоритетно в дискуссии, чтобы дать возможность другим выслушать мнение оппонента. Не упускайте из виду, сколько времени в разговоре занимает обсуждение точки зрения доминирующего участника.

Включайте всех в коммуникацию, уважая и делая все возможное чтобы облегчить участие людям, если:

  • для них язык, на котором вы общаетесь, не является родным
  • принадлежат другой культуре
  • используют местоимения, отличные от «он» или «она»
  • живут в других временных зонах
  • cталкиваются с любыми другими затруднениями при общении

Понимайте разные точки зрения

Наша цель не должна заключаться в том, чтобы «выиграть» в каждом разногласии или споре. Более продуктивная цель - быть открытыми для идей, которые делают лучше наши собственные идеи. Стремитесь быть примером разностороннего мышления. «Победа» - это когда разные точки зрения делают нашу работу лучше.

Уважайте наши сходства и различия

Сообщество ArtFlutter объединяет различные культуры и их различия могут охватывать все: от официальных религиозных обрядов до личных привычек в одежде. Будьте уважительны к людям с различными культурными практиками, позициями и убеждениями. Работайте над устранением собственных предубеждений, предрассудков и дискриминационного поведения. Используйте предпочтительный для людей способ обращения(включая местоимения) и соответствующий тон голоса. Уважайте право людей на неприкосновенность частной жизни и конфиденциальность. Будьте открыты для обучения других также как и для самообразования.

Подавайте пример

Сопоставляя свои действия с вашими словами, вы становитесь человеком на которого другие хотят быть похожими. Ваши действия влияют на поведение других людей и отражаются таким образом, что становятся очень ценным опытом для наших общих результатов. Создавайте в своем окружении условия для сплоченности. Будьте ответственны за свое поведение, а также привлекайте к этому других.

Неприемлемое поведение

Ниже перечислены виды поведения, которые считаются неприемлемыми в соответствии с нашими принципами.

Насилие и угрозы насилия

Угрозы насилия недопустимы ни в какой из форм. Это включает подстрекательство к насилию в отношении любого лица, или поощрение нанесения себе вреда человеком. А также сюда входит публикация или угроза публикации личной информации других лиц в Интернете.

Агрессия по отношению к человеку

Конфликты неизбежно возникнут, но никакое недовольство никогда не должно превращаться в личную агрессию. Нельзя оскорблять, унижать или принижать других. Нападки на кого-то за их мнение, убеждения или идеи неприемлемы. Когда мы с чем-то не согласны, или же считаем, что что-то можно сделать лучше - важно говорить об этом прямо. В таких обсуждениях необходимо сохранять профессионализм и уважение ко всем участникам, и не отходить от предмета дискуссии.

Темы для обсуждения, которых следует избегать

Неприемлемыми или оскорбительными считаются фразы, касающаяся:

  • Происхождения
  • Семейного статуса
  • Пола
  • Гендерной идентичности
  • Семейного положения
  • Сексуальной ориентации
  • Родного языка
  • Возраста
  • Инвалидности
  • Расы / этнической принадлежности
  • Национального происхождения
  • Социального статуса
  • Религии
  • Географического положения
  • Другие оскорбительные темы

Также неприемлемым считается намеренное обращение к кому-то с неидентифицированным полом, и / или подвергание сомнению гендерную самоидентификацию личности. Если вы не уверены, что слово не оскорбительно, не используйте его. Все это также касается косвенной дискриминации; Когда вас просят прекратить подобное поведение, сразу же сделайте это.Понимайте разные точки зрения

Наша цель не должна заключаться в том, чтобы «выиграть» в каждом разногласии или споре. Более продуктивная цель - быть открытыми для идей, которые делают лучше наши собственный идеи. Стремитесь быть примером разностороннего мышления. «Победа» - это когда разные точки зрения делают нашу работу лучше.

Подстрекательство

Мы будем относиться к подстрекательству так же, как к самому нарушению, и применять те же меры.

Последствия

Неприемлемое поведение любого члена сообщества, в том числе и с полномочиями по принятию решений не будет допускаться. Преднамеренные усилия по исключению людей неприемлемы и будут рассмотрены соответствующим образом.

Жалобы о преследовании/дискриминации будут быстро и тщательно изучаться. Будут приняты соответствующие меры для решения этой проблемы. Любой, кого попросили прекратить такого рода поведение, должен сразу же сделать это. Нарушение этих рекомендаций может привести к тому, что вас попросят покинуть помещение или онлайн-пространство временно или в течении всего события, быть недопущением к участию в мероприятиях, или же исключенным на неопределенный срок.

Представители проекта ArtFlutter также несут ответственность по всем вышеперечисленным пунктам. Представители ArtFlutter, нарушающие эти правила, будут подвергаться дисциплинарным наказаниям вплоть до прекращения сотрудичества с проектом ArtFlutter.

Кроме того, любые участники, которые злоупотребляют отчетами о нарушениях будут считаться нарушающими эти руководящие принципы со всеми вытекающими последствиями. Ложные сообщения, особенно из чувства мести, или же необоснованное исключение участника из офлайн или онлайн пространства являются недопустимыми.

Вопросы

Если у вас есть вопросы по этим рекомендациям, то мы настоятельно просим их озвучить. Если вы организуете мероприятие или активность, мы будем рады если вы обратитесь к нашим советам советам при планировании вступительной речи на вашем мероприятии или активности. Ваше мнение важно для нас и вы всегда получите ответ в течении 24 часов (либо в течении следующего буднего дня, если это выходные).

Лицензия

Creative Commons Attribution-ShareAlike license

Эти рекомендации были адаптированы к изменениям из Mozilla’s original Community Participation Guidelines, the Ubuntu Code of Conduct, Mozilla’s View Source Conference Code of Conduct, и the Rust Language Code of Conduct, который базирован на Stumptown Syndicate’s Citizen Code of Conduct. Дополнительный текст из LGBTQ in Technology Code of Conduct and the WisCon code of conduct.

С кем связаться Задать вопросы об этом наборе правил можно по адресу электронной почты [email protected] или в Telegram @vasilich6107

reactive_forms_generator's People

Contributors

benjifarquhar avatar doug-l-mitchell avatar ethras avatar furaiev avatar ibrahim-mubarak avatar ipcjs avatar tortillazhawaii avatar vasilich6107 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

reactive_forms_generator's Issues

Readme final example using wrong form model

The last example in the readme should use the FreezedClass model, not Tiny. As a result, the Freezed example is a bit less complete. I assume it is used the same way, however.

The casing of key names of Form value is different from what Freezed package generated

E.g:
final x = formModel.form.value

Result:
{ profileImage: "face.jpg" }

Desired Result:
{ profile_image: "face.jpg" }

This results from incompatibility of fromJson & toJson of Freezed Model

I already used @jsonkey(name: 'profile_image') and the result is still the same

Packages version:
freezed_annotation: ^2.0.3
json_annotation: ^4.6.0
reactive_forms: ^14.2.0
reactive_forms_annotations: ^1.0.0
build_runner: ^2.3.2
reactive_forms_generator: ^1.0.0
freezed: ^2.0.3
json_serializable: ^6.6.1

Form to openapi generator `dart-dio` model

Hello how would you build the builder object, in my case CreateDataset, for openapi generator dart-dio from a the formModel.form.value. The keys match the model fields. I have the following:

  Future<void> upload(Map<String, Object?> formValue) async {
    Response<DatasetResponse> response =
        await syndbStargateApi.registerNewDatasetStargateNeurodataStargateStargateNewDatasetPost(
            createDataset: CreateDataset()); 
    Future.microtask(() => context.read<UploadManifest>().datasetId = response.data!.datasetId);
  }

Not sure how to fill CreateDataset(), I tried CreateDataset((b) => formValue), which didn't work. Not sure what to do instead

Usually I would do something like this:

UserCreate(((b) => b
                        ..email = data.name!
                        ..password = data.password!))

But this form is nested, so I can't do it this way. Get the following error:

Error: Tried to build class "CreateDataset" but nested builder for field "tags" threw: Tried to build class "DatasetTags" but nested builder for field "animal" threw: Tried to construct class "SyndbApiNeurodataModelsAnimalLeaf" with null field "species". This is forbidden; to allow it, mark "species" with @nullable.

Which is not true:
image

Generated classes missing the resetValue member

Generated form model classes with @ReactiveFormAnnotation() producing missing implementation errors. The resetValue member of the FormModel<TModel> abstract class is ignored during code generation.

:Error: The non-abstract class 'TransferForm' is missing implementations for these members:
transfer_form_model.gform.dart:175
 - FormModel.resetValue
Try to either
 - provide an implementation,
 - inherit an implementation from a superclass or mixin,
 - mark the class as abstract, or
 - provide a 'noSuchMethod' implementation.

I noticed the 0.14.0-beta of reactive_forms_annotation does not include the resetValue member.

[Enhance] Sintax Linter Correction

Widgets constructor should be const,

ReactiveMigrantFormConsumer({Key? key, required this.builder, this.child}) : super(key: key);

These 3 packages are not used by gform class:

import 'package:reactive_forms/src/widgets/inherited_streamer.dart'; import 'dart:convert'; import 'package:reactive_forms_annotations/reactive_forms_annotations.dart';

support for generics?

Does this package have support for generic nested forms?

Following your example:

class DeliveryList {
  final List<DeliveryPoint> deliveryList;

  DeliveryList({
    this.deliveryList = const [],
  });
}

abstract class DeliveryPoint {
  final String name;

  final Address? address;

  DeliveryPoint({
    this.name = '',
    this.address,
  });
}

class PrivateDeliveryPoint extends DeliveryPoint{
final String personName;
..
}

class CompanyDeliveryPoint extends DeliveryPoint(
final String partnerId;
..
}

I really like the features so far but I'm stuck here.

Thanks for the help!

Bump reactive_forms

I assume this is on the roadmap to bump to reactive_forms 14. It is quite early still, my bad 😄

The reactive form model class cannot have a constructor with non-nullable fields

When the generated code constructs the model class it uses null propagation to get the corresponding property from the original model instance, thereby attempting to pass in a nullable version of the expected property type.

An example of where this issue can be seen is in login_extended. It's only not an issue in this instance because LoginExtended.unAnnotated is nullable, but if it's changed to non-nullable it would cause a compile time error.

The one example I've seen where it set the model as nullable, and therefore doesn't require null propagation when accessing it's properties, is in profile, but I don't know how to replicate.

ExtendedControl's value doesn't get updated when updating the formgroup manually

I am working on an app that requires caching and restoring user input, and I have a problem when using FormArrayAnnotation is that it creates a separate typed field which isn't synced with the original FormArray control
e.g.:
https://github.com/artflutter/reactive_forms_generator/blob/master/packages/reactive_forms_generator/example/lib/docs/animated_url_list/url.gform.dart#L188

when saving the form to Map<String,Object?> it works fine, but when restoring, I have to update the form using form.updateValue(cached); which doesn't update that separate field.

my suggestion is to remove this separate field, and instead infer its value from the current FormArray control

How to declare predefined validators?

I follow the doc to declare static method to get requiredValidator

Map<String, dynamic>? requiredValidator(AbstractControl<dynamic> control) {
  return Validators.required(control);
}

but I tried to implements also other methods
https://github.com/joanpablo/reactive_forms#predefined-validators

I've some lack of knowledge about declaring complex validators like min / max / equals / must match etc.
Do you think we could have those methods under the generated code? Otherwise could we have additional doc or resources in doc to implements those methods?

Thank you in advance for your help.

Shared models are generated twice

consider this code:
data.dart:

part 'data.gform.dart';
@FormGroupAnnotation()
class DataDefModel {
  final String? id;
  final String name;

  const DataDefModel({
    @FormControlAnnotation() required this.id,
    @FormControlAnnotation() required this.name,
  });
}

a.dart:

import 'data.dart';
part 'a.gform.dart';

@ReactiveFormAnnotation()
@FormGroupAnnotation()
class AModel {
  final List<DataDefModel> dataDefs;

  const AModel({
    @FormArrayAnnotation() required this.dataDefs,
  });
}

b.dart:

import 'data.dart';
part 'b.gform.dart';

@ReactiveFormAnnotation()
@FormGroupAnnotation()
class BModel {
  final List<DataDefModel> dataDefs;

  const BModel({
    @FormArrayAnnotation() required this.dataDefs,
  });
}

this will generate DataDefModelForm 3 times (in data.gform.dart, a.gform.dart and b.gform.dart) which will lead to 3 different versions of the same class
cc: @vasilich6107

generated FormBuilder should call `initState` on `didUpdateWidget`

example: https://github.com/artflutter/reactive_forms_generator/blob/master/generator_tests/test/doc/login_extended_test.dart#L275-L286

@override
void didUpdateWidget(covariant LoginExtendedFormBuilder oldWidget) {
  if (widget.model != oldWidget.model) {
    _formModel = LoginExtendedForm(
        widget.model, LoginExtendedForm.formElements(widget.model), null);
    if (_formModel.form.disabled) {
      _formModel.form.markAsDisabled();
    }
  }
  super.didUpdateWidget(oldWidget);
}

when this gets called, the _formModel is replaced with a new value, but it doesn't get passed to the initState parameter, this makes the developer unaware of changes to the formModel

proposed solution:

@override
void didUpdateWidget(covariant LoginExtendedFormBuilder oldWidget) {
  if (widget.model != oldWidget.model) {
    _formModel = LoginExtendedForm(
        widget.model, LoginExtendedForm.formElements(widget.model), null);
    if (_formModel.form.disabled) {
      _formModel.form.markAsDisabled();
    }
+    widget.initState?.call(context, _formModel);

  }
  super.didUpdateWidget(oldWidget);
}

Custom getters with freezed throw errors with dartz

Hi,

Before starting, thank you for this awesome generator that prevent boilerplate to create forms! 💯

I'm using freezed to create my models and I've an issue when I'm adding a custom getter on freezed. The Reactive form annotation throw errors.

Here a sample:

import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:reactive_forms_annotations/reactive_forms_annotations.dart';

part 'test.freezed.dart';

@freezed
@ReactiveFormAnnotation()
class Test with _$Test {
  const Test._();
  const factory Test({
    @FormControlAnnotation() required String title,
    @FormControlAnnotation() String? description,
  }) = _Test;

  factory Test.empty() => Test(
        title: '',
        description: '',
      );
  // The error occurs with this grrr
  Option<String> get failureOption {
    if (title.isEmpty) {
      return some('Cannot be empty');
    } else {
      return none();
    }
  }
}

It's working fine without this getter.
Also, it's working with a simple getter

String get yolooo => 'Yoloo';

Option is from dartz module, the error occurs when importing on this freezed class.
The error occurred when instantiating the form in a widget.

final form = TestFormBuilder(
      model: Test.empty(),
      builder: (context, formModel, child) {
        return Column(children: [
          const SizedBox(height: 16),
          ReactiveTextField<String>(
            formControl: formModel.titleControl,
            validationMessages: (control) => {
              ValidationMessage.required: 'Required',
            },
            decoration: InputDecoration(labelText: 'Title'),
          ),
        ]);
      },
    );

Let me know if you need additional information.

Version used
dependencies
reactive_forms: ^10.6.4
reactive_forms_annotations: ^0.1.0-beta

dev dependencies
reactive_forms_generator: ^0.3.2-beta

Duplicates in generated code

Had random duplicates of methods throughout the entire generated code. Removing the duplicates hopefully solves the issue, will test tomorrow. Had ~20 duplicate methods.

import 'package:reactive_forms_annotations/reactive_forms_annotations.dart';

part 'forms.gform.dart';

Map<String, dynamic>? requiredValidator(AbstractControl<dynamic> control) {
  return Validators.required(control);
}

@ReactiveFormAnnotation()
class CreateProject {
  final String doi;
  final String? label;
  final String? notes;

  final List<String>? datasetIds;

  CreateProject(
      {@FormControlAnnotation(validators: [requiredValidator]) this.doi = '',
      @FormControlAnnotation() this.label,
      @FormControlAnnotation() this.notes,
      @FormControlAnnotation() this.datasetIds});
}

@ReactiveFormAnnotation()
class CreateDataset {
  final String? label;
  final String? notes;

  final Animal? animal;
  final List<TargetedMutation> targetedMutation;
  final MicroscopyMethod? microscopyMethod;
  final BrainStructure? brainStructure;

  CreateDataset(
      {@FormControlAnnotation() this.label,
      @FormControlAnnotation() this.notes,
      @FormControlAnnotation() this.animal,
      @FormArrayAnnotation() this.targetedMutation = const [], // note diff
      @FormControlAnnotation() this.microscopyMethod,
      @FormControlAnnotation() this.brainStructure});
}

@FormGroupAnnotation()
class Animal {
  final String species;

  Animal({@FormControlAnnotation(validators: [requiredValidator]) this.species = ''});
}

@FormGroupAnnotation()
class TargetedMutation {
  final String gene;
  final bool isKnockOut;
  final String method;

  TargetedMutation(
      {@FormControlAnnotation(validators: [requiredValidator]) this.gene = '',
      @FormControlAnnotation() this.isKnockOut = false,
      @FormControlAnnotation(validators: [requiredValidator]) this.method = ''});
}

@FormGroupAnnotation()
class MicroscopyMethod {
  final String name;

  MicroscopyMethod({@FormControlAnnotation(validators: [requiredValidator]) this.name = ''});
}

class BrainStructure {
  final String name;

  BrainStructure({@FormControlAnnotation(validators: [requiredValidator]) this.name = ''});
}

Excerpt from generator:

  String labelControlPath() => pathBuilder(labelControlName);
  String notesControlPath() => pathBuilder(notesControlName);
  String animalControlPath() => pathBuilder(animalControlName);
  String microscopyMethodControlPath() =>
      pathBuilder(microscopyMethodControlName);
  String brainStructureControlPath() => pathBuilder(brainStructureControlName);
  String animalControlPath() => pathBuilder(animalControlName);
  String microscopyMethodControlPath() =>
      pathBuilder(microscopyMethodControlName);
  Object? get labelErrors => labelControl?.errors;
  Object? get notesErrors => notesControl?.errors;
  Object? get animalErrors => animalControl?.errors;
  Object? get microscopyMethodErrors => microscopyMethodControl?.errors;
  Object? get brainStructureErrors => brainStructureControl?.errors;
  Object? get animalErrors => animalControl?.errors;
  Object? get microscopyMethodErrors => microscopyMethodControl?.errors;
  Object? get targetedMutationErrors => targetedMutationControl.errors;
  void get labelFocus => form.focus(labelControlPath());
  void get notesFocus => form.focus(notesControlPath());
  void get animalFocus => form.focus(animalControlPath());
  void get microscopyMethodFocus => form.focus(microscopyMethodControlPath());
  void get brainStructureFocus => form.focus(brainStructureControlPath());
  void get animalFocus => form.focus(animalControlPath());
  void get microscopyMethodFocus => form.focus(microscopyMethodControlPath());
  void get targetedMutationFocus => form.focus(targetedMutationControlPath());

Generic Form

Is it possible to add a generic type to a form?

Like:

part 'tags_form_definition.freezed.dart';
part 'tags_form_definition.gform.dart';

@freezed
@ReactiveFormAnnotation()
class Tags<T> with _$Tags<T> {
  factory Tags({
    @FormControlAnnotation(
      validators: [VpValidators.optionSelected],
    )
        required List<T>? tags,
  }) = _Tags;

  const Tags._();
}

I'm getting a cast error here:

      groceryItm: (groceryItm) => groceryItm.copyWith(
          tags: (form as TagsForm).model.tags as List<GroceryItmTag>?,

Because it is actually List<dynamic> due to:

part 'tags_form_definition.freezed.dart';
part 'tags_form_definition.gform.dart';

@freezed
@ReactiveFormAnnotation()
class Tags with _$Tags {
  factory Tags({
    @FormControlAnnotation(
      validators: [VpValidators.optionSelected],
    )
        required List? tags,
  }) = _Tags;

  const Tags._();
}

As the form is reused with List<GroceryItmTag>, List<MenuItmTag>, and List<FashionItmTag>

Even though the concrete value is List<GroceryItmTag>, it cannot be assigned to a field of type List<GroceryItemTag>:

Screenshot 2022-09-26 at 8 13 13 PM

Validators set programmatically get cleared on didUpdateWidget

I have a non-const validator that will never be const because it gets passed an object which represents the current user of the app.

I have set the validator programmatically like this:

    formModel.form.setValidators([
      VpValidators.mustMatchIf(
        SettingsForm.emailAddressControlName,
        SettingsForm.confirmEmailAddressControlName,
        () => isEmailEdited(
          ref!.watch(userProvider).value!.emailAddress!,
          formModel.model,
        ),
      )
    ]);

But in didUpdateWidget, the line final elements = _formModel.formElements(); causes clearValidators() to run and clears all the validators and then my code which adds the vallidator programmatically does not re-run. Any ideas how I can either stop clearing the validators or re-run my setValidators code on didUpdateWidget?

Make the auto-generated form class implement a minimal abstract class

Do you think you could make the auto-generated form implement a minimal abstract class since they all do anyway? They just don't have the implements keyword, which means we can't use the interface in our code:

abstract class FormModel<TModel> {
  FormModel({required this.form});
  final FormGroup form;
  TModel get model;
}

For example, I have this generated code:

class UserCredentialForm {

And would like it to generate this instead:

class UserCredentialForm implements FormModel<UserCredential> {

It would allow me to do this:


/// The base form view model which all other forms extend.
abstract class FormVm<TModel, TSubmitResponse> {
  FormVm({required this.$provider}) {
    $onInit();
  }

  @protected
  void $onInit() {}

  late FormModel<TModel> $formModel; // <===== see this
  late TModel $initialValue;

  @mustCallSuper
  void $onDestroy() {
    $formModel.form.markAsUntouched();// <===== here
  }
  
    Future<SuccessResult<TSubmitResponse>?> $saveMethod() async {
    return await ($formModel.model as DomainEntity).$save()// <===== here
        as Future<SuccessResult<TSubmitResponse>?>;
  }

Provider / Riverpod plugin

The reactive_forms package says that reactive_forms can be used very well in combination with the provider plugin to separate the Ui logic and business logic. A FormGroup can be created within the business logic. Thus, the form can also be manipulated elegantly from the business logic at any time as desired.

Is there also the possibility to manipulate the form from the business logic when using reactive_forms_generator? Unfortunately I have not found a corresponding example.

Rerendering the FormBuilder with a new FormModel not visually updating TextField

Hi, I don't think there is a bug in the library, it is probably a bug in my code, I just wanted to see if anyone knows what could be going on here.

I am manually updating the FormModel of my widget, and the Textfields are not displaying the new value that is in the new FormModel. I can see that the FormModel is updated by inspecting this code with the debugger, which returns the widgets, but visually, the text field values do not update:

  @override
  List<Widget> detailsWidgets(WidgetRef wProvider, VgnItmLocalFormVm vm) {
    return [
      Expanded(
          child: DetailsFormBuilder(
              initState: (context, formModel) {
                vm.initState(context, formModel: formModel);
              },
              model: (vm as DetailsFormVm).formModel?.model ?? vm.initialValue,
              builder: (context, formModel, child) {
                vm.formModel = formModel;
                vm.initState(context, formModel: formModel);
                return Column(children: [
                  Expanded(
                      child: KeyboardActions(
                          tapOutsideBehavior: TapOutsideBehavior.opaqueDismiss,
                          keepFocusOnTappingNode: true,
                          config: KeyboardActionsConfig(
                            defaultDoneWidget: const VpKeyboardDoneButton(),
                            actions: [
                              KeyboardActionsItem(
                                focusNode: focusNodeName,
                              ),
                              KeyboardActionsItem(
                                  focusNode: focusNodeCompanyName,
                                  onTapAction: () {
                                    vm.formModel!.form.focus(
                                        DetailsForm.descriptionControlName);
                                  }),
                              KeyboardActionsItem(
                                  focusNode: focusNodeDescription,
                                  onTapAction: () {}),
                            ],
                          ),
                          child: Padding(
                              padding: formParentPadding(context),
                              child: Column(children: [
                                nameTextField(wProvider, vm),
                                const SizedBox(height: INPUT_VERTICAL_PAD),
                                companyNameTextField(vm),
                                const SizedBox(height: INPUT_VERTICAL_PAD),
                                descriptionTextField(context, vm),
                              ])))),
                  Visibility(
                      visible: manageVgnItmsPageVm.shouldDisplayFooter,
                      child: Center(child: vm.submitButton(context)))
                ]);
              }))
    ];
  }
  
    Widget descriptionTextField(BuildContext context, VgnItmLocalFormVm vm) {
    return VpTextField(
      maxLines: null,
      form: vm.formModel?.form,
      focusNode: focusNodeDescription,

      textInputType: TextInputType.multiline,
      textInputAction: TextInputAction.newline,
      // onSubmitted: (control) {
      //   shouldSubmitFromDescription ? detailsVm?.save(context) : null;
      // },
      maxLength: MAX_LENGTH_L,
      labelText: 'Description',
      formControlName: DetailsForm.descriptionControlName,
      hintText: descriptionHint,
    );
  }

If I manually completely remove the widget on the screen by clicking away, and then add it back in, it is now rendered with the correct updated value. Any ideas what is going on here?

Annotation using material design

Hi,

When performing tests on Dart package (no UI only repository) I've this issue.

✓ Optimizing tests (4.2s)
package:reactive_forms_annotations has `uses-material-design: true` set but the primary pubspec contains `uses-material-design: false`. If the application needs material icons, then `uses-material-design`  must be set to true.

Could we change the pubspec.yaml to uses-material-design: false?
https://github.com/artflutter/reactive_forms_generator/blob/master/packages/reactive_forms_annotations/pubspec.yaml
and
https://github.com/artflutter/reactive_forms_generator/blob/master/packages/reactive_forms_generator/pubspec.yaml

I've checked the generator and seems no material icons used and must not be used in the future. Do you confirm?

Thank you for your feedback @vasilich6107

Example of a ReactiveFormsGenerator on ViewModel

I have this form class (generated by ReactiveFormsGenerator) called ReactiveProductForm,

can you please provide an example where ReactiveProductForm is placed on ViewModel and being called on View?

I already check the docs, particulary the
add_dynamic_controls_sample.dart

but it only shows generic reactive form :(

It would be very helpful if you can provide an example, Thanks!

Issue of example

Hi,
I run the same example of TinyForm and the output was:

Try removing the extra positional arguments.
                          e.ParameterElementImpl('FakeParameterElement', 20)
                                                ^
../../flutter/flutter/.pub-cache/hosted/pub.dartlang.org/analyzer-2.2.0/lib/src/dart/element/element.dart:4862:3: Context: Found this candidate, but the arguments don't match.
  ParameterElementImpl({
  ^^^^^^^^^^^^^^^^^^^^
[INFO] Precompiling build script... completed, took 1.1s

[SEVERE] Failed to precompile build script .dart_tool/build/entrypoint/build.dart.
This is likely caused by a misconfigured builder definition.

pub finished with exit code 78```

I'm running flutter 2.5.
I run build_runner clean and flutter clean but got the same error.
My Tiny Class is:
```@ReactiveFormAnnotation()
class Tiny {
  final String email;

  final String password;

  Tiny({
    @FormControlAnnotation() this.email = '',
    @FormControlAnnotation() this.password = '',
  });
}```

I'm running on the latest version of the generator.

The static formControlNames could be const

Pretty minor but I noticed the generated formcontrol names e.g.

class SettingsForm implements FormModel<Settings> {
  SettingsForm(
    this.settings,
    this.form,
    this.path,
  ) {}

  static String emailAddressControlName = "emailAddress"; <============================= this

are not const and could be.

It would make them usable in, for example, the mustMatch validator in the example project:

  Map<String, dynamic>? mustMatch(AbstractControl<dynamic> control) {
  const email = 'email';
  const password = 'password';

  final form = control as FormGroup;

  final formControl = form.control(email);
  final matchingFormControl = form.control(password);

  if (formControl.value != matchingFormControl.value) {
    matchingFormControl.setErrors(<String, dynamic>{
      ...matchingFormControl.errors,
      ...<String, dynamic>{'mustMatch': true},
    });

    // force messages to show up as soon as possible
    matchingFormControl.markAsTouched();
  } else {
    matchingFormControl.removeError('mustMatch');
  }

  return null;
}

Actually I have just realised they are already usable in the above code example, just removing the const key work, so not sure if it is worth it.

User profile form not running

I was running the example code. But when I click User profile section , received following error.
Screenshot at 2022-10-16 15-29-08

I am running flutter version 3.3.4.

I found that the error is because of the address type.

Error:-

  ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
                     The following assertion was thrown building ReactiveFormBuilder(dirty, state:
                     ReactiveFormBuilderState#4795d):
                     Must provide a formControlName or a formControl, but not both at the same time.
                     'package:reactive_forms/src/widgets/reactive_form_field.dart':
                     Failed assertion: line 73 pos 13: '(formControlName != null && formControl == null) ||
                                     (formControlName == null && formControl != null)'
                     
                     The relevant error-causing widget was:
                       ReactiveFormBuilder

ReactiveFormBuilder:file:///home/sumit/AndroidProjects/reactive_forms_generator/packages/reactive_forms_generator/example/lib/docs
                       /user_profile/user_profile.gform.dart:157:14
                     
                     When the exception was thrown, this was the stack:
                     #2      new ReactiveFormField (package:reactive_forms/src/widgets/reactive_form_field.dart:73:13)
                     #3      new ReactiveTextField (package:reactive_forms/src/widgets/reactive_text_field.dart:145:9)
                     #4      UserProfileFormWidget.build.<anonymous closure>
(package:example/docs/user_profile/user_profile_form.dart:57:19)
                     #5      _UserProfileFormBuilderState.build.<anonymous closure>
(package:example/docs/user_profile/user_profile.gform.dart:161:27)
                     #6      ReactiveFormBuilderState.build (package:reactive_forms/src/widgets/reactive_form_builder.dart:85:28)
                     #7      StatefulElement.build (package:flutter/src/widgets/framework.dart:4992:27)
                     #8      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4878:15)
                     #9      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
                     #10     Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
                     #11     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4859:5)
                     #12     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5041:11)
                     #13     ComponentElement.mount (package:flutter/src/widgets/framework.dart:4853:5)
                     ...     Normal element mounting (170 frames)
                     #183    Element.inflateWidget (package:flutter/src/widgets/framework.dart:3863:16)
                     #184    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6435:36)
                     #185    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6447:32)
                     ...     Normal element mounting (391 frames)
                     #576    Element.inflateWidget (package:flutter/src/widgets/framework.dart:3863:16)
                     #577    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6435:36)
                     #578    Element.updateChild (package:flutter/src/widgets/framework.dart:3592:18)
                     #579    RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5964:32)
                     #580    MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6460:17)
                     #581    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #582    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
                     #583    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
                     #584    Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
                     #585    StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
                     #586    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #587    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
                     #588    Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
                     #589    ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
                     #590    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #591    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
                     #592    Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
                     #593    ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
                     #594    _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:107:11)
                     #595    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #596    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6307:14)
                     #597    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #598    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
                     #599    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
                     #600    Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
                     #601    StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
                     #602    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #603    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6307:14)
                     #604    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #605    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6307:14)
                     #606    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #607    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
                     #608    Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
                     #609    ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
                     #610    Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
                     #611    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
                     #612    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
                     #613    Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
                     #614    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2667:19)
                     #615    WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
                     #616    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:378:5)
                     #617    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1175:15)
                     #618    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1104:9)
                     #619    SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1015:5)
                     #620    _invoke (dart:ui/hooks.dart:148:13)
                     #621    PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
                     #622    _drawFrame (dart:ui/hooks.dart:115:31)
                     (elided 2 frames from class _AssertionError)
                     
                     ════════════════════════════════════════════════════════════════════════════════════════════════════
                     ```

Form array error

Using latest version 0.3.
Flutter 2.5.1

The problems is when generating paths:

For creating forms:

migrantRecruitmentDataRecruitmentDataForm =
        (migrant.migrantRecruitmentData ?? [])
            .asMap()
            .map(
              (k, v) => MapEntry(
                k,
                RecruitmentDataForm(
                  recruitmentData: v,
                  form: form,
                  path: pathBuilder("migrantRecruitmentData.$k"),
                ),
              ),
            )
            .values
            .toList();

For retrieving:

List<RecruitmentData>? get migrantRecruitmentDataValue =>
      (migrantRecruitmentDataControl.value ?? [])
          .asMap()
          .map((k, v) => MapEntry(
              k,
              RecruitmentDataForm(
                      recruitmentData: RecruitmentData(),
                      form: form,
                      path: pathBuilder("deliveryList.$k"))
                  .model))
          .values
          .toList();

The path builder should be the same.
Also when generating the class name, it is repeated RecruitmentDataRecruitmentDataForm should be RecruitmentDataForm.

How to get form value from `builder` instance?

I have the following widget, but I can't figure out how to get the value of the form.

The widget of concern is:

ElevatedButton(
                onPressed: () {
                  setState(() => form.model);
                },
                child: const Text('Save'),
              )

form.model is just a map with null values even though the form has defined fields; form.form is not defined, which is the example in the package.

Sub-request: Can you also address examples with Key key? Are we supposed to use GlobalKey<FormState>, that is what I have been doing, not sure if this is correct.

Whole widget:

class NewDatasetWidget extends StatefulWidget {
  const NewDatasetWidget({Key? key}) : super(key: key);

  @override
  NewDatasetWidgetState createState() => NewDatasetWidgetState();
}

class NewDatasetWidgetState extends State<NewDatasetWidget> {
  @override
  Widget build(BuildContext context) {
    final CreateDatasetFormBuilder form = CreateDatasetFormBuilder(
        key: context.read<UploadManifest>().newDatasetFormKey,
        model: CreateDataset(),
        builder: (BuildContext context, CreateDatasetForm formModel, Widget? child) {
          return Column(children: [
            ReactiveTextField<String>(
              formControl: formModel.labelControl,
              validationMessages: {
                ValidationMessage.required: (error) => "Must not be empty",
              },
              decoration: const InputDecoration(
                labelText: 'Label',
              ),
            ),
            columnSpacer,
            ReactiveTextField<String>(
              formControl: formModel.notesControl,
              validationMessages: {
                ValidationMessage.required: (error) => "Must not be empty",
              },
              decoration: const InputDecoration(
                labelText: 'Notes',
              ),
            ),
            columnSpacer,
            ReactiveTextField<String>(
              formControl: formModel.tagsForm.microscopyMethodForm.nameControl,
              validationMessages: {
                ValidationMessage.required: (error) => "Must not be empty",
              },
              decoration: const InputDecoration(
                labelText: 'Microscopy method',
              ),
            ),
            columnSpacer,
            ReactiveTextField<String>(
              formControl: formModel.tagsForm.animalForm.speciesControl,
              validationMessages: {
                ValidationMessage.required: (error) => "Must not be empty",
              },
              decoration: const InputDecoration(
                labelText: 'Animal Species',
              ),
            ),
            columnSpacer,
            ReactiveFormArray<Map<String, Object?>>(
              formArray: formModel.tagsForm.targetedMutationControl,
              builder: (context, formArray, child) => Column(children: [
                ...formArray.value!
                    .asMap()
                    .map((i, targetedMutation) => MapEntry(
                        i,
                        Card(
                            child: Padding(
                                padding: const EdgeInsets.symmetric(vertical: widgetSpacing),
                                child: ListTile(
                                    leading: const Icon(Icons.table_chart_rounded),
                                    title: Text("Gene ${i + 1}"),
                                    subtitle: Wrap(
                                      spacing: widgetSpacing,
                                      runSpacing: widgetSpacing,
                                      children: [
                                        ReactiveTextField<String>(
                                          formControlName: '$i.gene',
                                          validationMessages: {
                                            ValidationMessage.required: (error) =>
                                                "Must not be empty"
                                          },
                                          decoration: const InputDecoration(
                                            labelText: 'Gene name',
                                          ),
                                        ),
                                        ReactiveCheckboxListTile(
                                          formControlName: '$i.isKnockOut',
                                          title: const Text('Knock-out'),
                                          subtitle: const Text('Knock-in if left unchecked'),
                                        ),
                                        ReactiveTextField<String>(
                                          formControlName: '$i.method',
                                          validationMessages: {
                                            ValidationMessage.required: (error) =>
                                                "Must not be empty"
                                          },
                                          decoration: const InputDecoration(
                                            labelText: 'Method',
                                          ),
                                        )
                                      ],
                                    ),
                                    trailing: TextButton(
                                      child: const Text('Delete'),
                                      onPressed: () =>
                                          formModel.tagsForm.removeTargetedMutationItemAtIndex(i),
                                    ))))))
                    .values
                    .toList(),
                columnSpacer,
                ElevatedButton(
                  onPressed: () {
                    setState(() => formModel.tagsForm.addTargetedMutationItem(TargetedMutation()));
                  },
                  child: const Text('Add Targeted Mutation'),
                )
              ]),
            ),
          ]);
        });
    return Scaffold(
        primary: false,
        appBar: AppBar(
          title: const Text('New Dataset'),
        ),
        body: Padding(
            padding: const EdgeInsets.all(widgetSpacing),
            child: Column(children: [
              form,
              columnSpacer,
              ElevatedButton(
                onPressed: () {
                  setState(() => form.model);
                },
                child: const Text('Save'),
              )
            ])));
  }
}

This is my model:

import 'package:reactive_forms_annotations/reactive_forms_annotations.dart';

part 'forms.gform.dart';

Map<String, dynamic>? requiredValidator(AbstractControl<dynamic> control) {
  return Validators.required(control);
}

@ReactiveFormAnnotation()
class CreateProject {
  final String doi;
  final String? label;
  final String? notes;

  final List<String>? datasetIds;

  CreateProject(
      {@FormControlAnnotation(validators: [requiredValidator]) this.doi = '',
      @FormControlAnnotation() this.label,
      @FormControlAnnotation() this.notes,
      @FormControlAnnotation() this.datasetIds});
}

@ReactiveFormAnnotation()
class CreateDataset {
  final String? label;
  final String? notes;

  final DatasetTags? tags;

  CreateDataset(
      {@FormControlAnnotation() this.label, @FormControlAnnotation() this.notes, this.tags});
}

@FormGroupAnnotation()
class DatasetTags {
  final Animal? animal;
  final List<TargetedMutation> targetedMutation;
  final MicroscopyMethod? microscopyMethod;
  final BrainStructure? brainStructure;

  DatasetTags(
      {this.animal,
      @FormArrayAnnotation() this.targetedMutation = const [], // note diff
      this.microscopyMethod,
      this.brainStructure});
}

@FormGroupAnnotation()
class Animal {
  final String species;

  Animal({@FormControlAnnotation(validators: [requiredValidator]) this.species = ''});
}

@FormGroupAnnotation()
class TargetedMutation {
  final String gene;
  final bool isKnockOut;
  final String method;

  TargetedMutation(
      {@FormControlAnnotation(validators: [requiredValidator]) this.gene = '',
      @FormControlAnnotation() this.isKnockOut = false,
      @FormControlAnnotation(validators: [requiredValidator]) this.method = ''});
}

@FormGroupAnnotation()
class MicroscopyMethod {
  final String name;

  MicroscopyMethod({@FormControlAnnotation(validators: [requiredValidator]) this.name = ''});
}

class BrainStructure {
  final String name;

  BrainStructure({@FormControlAnnotation(validators: [requiredValidator]) this.name = ''});
}

Nested FormGroups "contains" functions return false when they should return true

The code below returns false when the homeForm does contain a control called "city".

builder: (context, formModel, child) {
  if (formModel.homeForm.containsCity) {
    print('Issue is fixed, well done!');
  }
}

Test it in the GroupContainsFormWidget, which can be viewed in the app in the "Group contains test" menu item of the example app.

[proposal] Make generated file as a `part` of the base file

Currently, we need to import both base and generated files:

import 'login.dart';
import 'login.gform.dart';

Let's make it as in built_value or freezed use part / part of.

// base
part 'login.gform.dart';

// generated
part of 'login.dart';

In this case, we can import only the base file.

get Model error

I discover an error on the get model method.
If I use a class where not all the fields are mark as FormControlAnotation() the build runner use getters that does not exist.
Example:
class TinyForm{ @FormControlAnnotation() this.name, this.lastNameFather, }

Then the builder generates:

Tiny get model => Tiny( name: nameValue, => this exist lastNameFather: lastNameFatherValue, => this does not exist. The compiler does not warm before execution because the analyzer omits the files on the config recommended )

Setting focus to a control programmatically doesn't work unless you physically focus a field first.

Paste this code into packages/reactive_forms_generator/example/lib/docs/login/login_form.dart and run it, then go to the "Login Form". Then without clicking on a form control, hit the "Submit" button. It should focus the password control but doesn't.

Note that if you click on a field first, it will begin to work.

import 'package:example/docs/login/login.dart';
import 'package:example/sample_screen.dart';
import 'package:flutter/material.dart' hide ProgressIndicator;
import 'package:reactive_forms_annotations/reactive_forms_annotations.dart';

class LoginFormWidget extends StatelessWidget {
  const LoginFormWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SampleScreen(
      title: const Text('Login'),
      body: BasicFormBuilder(
        initState: (context, formModel) {
          formModel.passwordFocus;
          formModel.form.focus('password');
        },
        builder: (context, formModel, child) {
          return Column(
            children: [
              ReactiveTextField<String>(
                formControl: formModel.emailControl,
                validationMessages: {
                  ValidationMessage.required: (_) => 'Required'
                },
                showErrors: (_) => false,
                decoration: const InputDecoration(
                  labelText: 'Email',
                  helperText: '',
                  helperStyle: TextStyle(height: 0.7),
                  errorStyle: TextStyle(height: 0.7),
                ),
              ),
              const SizedBox(height: 8.0),
              ReactiveTextField<String>(
                formControl: formModel.passwordControl,
                obscureText: true,
                showErrors: (_) => false,
                validationMessages: {
                  ValidationMessage.required: (_) => 'Required'
                },
                textInputAction: TextInputAction.done,
                decoration: const InputDecoration(
                  labelText: 'Password',
                  helperText: '',
                  helperStyle: TextStyle(height: 0.7),
                  errorStyle: TextStyle(height: 0.7),
                ),
              ),
              ReactiveBasicFormConsumer(
                builder: (context, formModel, child) {
                  // debugPrint(formModel.passwordControl.errors);
                  // debugPrint(formModel.form);
                  debugPrint('dirty => ${formModel.form.dirty}');
                  debugPrint(
                      'passwordDirty => ${formModel.passwordControl.dirty}');

                  return Text(formModel.emailControl.errors.toString());
                },
              ),
              const SizedBox(height: 8.0),
              ReactiveBasicFormConsumer(
                builder: (context, formModel, child) {
                  return ElevatedButton(
                    onPressed: () {
                      formModel.passwordFocus;
                    },
                    child: const Text('Submit'),
                  );
                },
              ),
            ],
          );
        },
      ),
    );
  }
}

Calling the nested FormGroups' "remove" functions does not remove the nested forms' controls

The code below does not enter inside the if statement (didn't use "contained" as that is another bug).

builder: (context, formModel, child) {
  formModel.homeForm.cityRemove();
  if (formModel.homeForm.cityControl == null) {
    print('You fixed removing a nested form\'s control. Good job!');
  }
}

Test it in the GroupRemoveFormWidget, which can be viewed in the app in the "Group remove test" menu item of the example app.

How to populate the form from some business logic/repository pattern?

Hello

I need to populate the form from Cubit/Repository classes in order to edit/update certain data in the repository.

is there some annotations to indicate initial State and to integrate business logic (BlocBuilder in this case) in the form?
is there some similar example in some link?

Thanks in advance
Regards

Support Freezed classes

Hello,

I'll start by saying I love the idea of the package. However, I use Freezed to generate my data classes.
I tried adding the generator to a freezed class but the properties of factory constructors don't seem to get picked up.

As an example here is the freezed class:

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:reactive_forms_annotations/reactive_forms_annotations.dart';

part 'test.g.dart';
part 'test.freezed.dart';

@freezed
@ReactiveFormAnnotation()
class Test with _$Test {
  const factory Test({
    @FormControlAnnotation() String? id,
    @FormControlAnnotation() String? name,
    @FormControlAnnotation() double? year,
  }) = _Test;

  factory Test.fromJson(Map<String, dynamic> json) => _$TestFromJson(json);
}

The expected behaviour would be something like:

class TestForm {
  TestForm(this.test, this.form, this.path) {}

  final Test test;

  final FormGroup form;

  final String? path;

  Test get model => Test();
  FormGroup formElements() => FormGroup({
        'id': FormControl<String>(),
        'name': FormControl<String>(),
        'year': FormControl<double>(),
      },
          validators: [],
          asyncValidators: [],
          asyncValidatorsDebounceTime: 250,
          disabled: false);
}

Would you consider adding support for this in the near future ?

How to use non-const validators

What's the best way to use a validator that takes an argument like a min or max?

the validators in the .gform file force it to be a const, so this has a compile time error:

      @FormControlAnnotation(
        validators: [VpValidators.minDuration(Duration(seconds: 10))],
      )

New FormModel.reset method behaves slightly different to FormGroup equivalent

I wonder if it would be more intuitive to make formModel.reset() to reset the form to null, and allow the model to be passed in if we want the model to remain populated, like the current FormGroup.reset does. What do you think? If we want only one option, and no extra arguments, I would think that formModel.reset should just reset the form to null, as that is what I would expect it to do.

0.23.2 causing an infinite loop

0.23.2 is causing my widgets to rebuild in an infinite loop.

I'm very perplexed by this issue as 0.23.1 works fine. When I deleted didUpdateWidget on 0.23.2 it works fine. I have to hardcode my version to 0.23.1 for now. I will try to reproduce when I have time. Leaving this here in case anyone has any ideas what it could be.

Remove() functions cause FormControlNotFoundException when accessing FormModel.model

For the scenario where you want to re-use a form model but without some fields, we have the generated remove functions, e.g.:

  void passwordRemove({bool updateParent = true, bool emitEvent = true}) =>
      form.removeControl(passwordControlPath(),
          updateParent: updateParent, emitEvent: emitEvent);

When you use them:

  void setUpForm() {
    formModel.passwordRemove();
  }

Then try to access formModel.model, you get a FormControlNotFoundException.

Not supporting analyzer 3

Could we upgrade to last version of analyzer on reactive_forms_generator to prevent breaking existing dependencies please?

Cant add validators to freezed classes.

Cant seem to workout what to do to get it accept validators. It would be nice to have a more concrete example.

Example code and error

The values in a const list literal must be constants.
Try removing the keyword 'const' from the list literal.dartnon_constant_list_element
Arguments of a constant creation must be constant expressions.
Try making the argument a valid constant, or use 'new' to call the constructor.dartconst_with_non_constant_argument

/// [SignInFormModel] data class.
@Freezed()
@ReactiveFormAnnotation()
class SignInFormModel with _$SignInFormModel {
  SignInFormModel._();

  const factory SignInFormModel({
    @FormControlAnnotation(
      validators: [Validators.required],
    )
        String? email,
    @FormControlAnnotation()
        String? password,
  }) = _SignInFormModel;

  factory SignInFormModel.fromJson(Map<String, dynamic> json) => _$SignInFormModelFromJson(json);
}

reactive_forms_annotations and reactive_forms_generator are pretty stable

Hi @vasilich6107

This repos is pretty stable, used more than 1 year now :) thank you for this great job!

Are we able to remove the -beta tag? To let us to use the convention of dart numbering and allow us to add rules like constraints resolution and use tools like show dependency version to get an overview of which version we need to upgrade

image

Source: https://dart.dev/tools/pub/versioning

Thank you for your feedback and your action on this topic!

Applicable to reactive_forms_annotations and reactive_forms_generator

FormArrayAnnotation validators have mismatching type

when generating a model with @FormArrayAnnotation<TModel> (not just @FormArrayAnnotation()) the validators assume it's a FormArray<TModel>, but the generated code is always FormArray<Map<String, Object?>>

e.g.:
if this was @FormArrayAnnotation<UrlEntity>(validators: [someValidator])
https://github.com/artflutter/reactive_forms_generator/blob/master/packages/reactive_forms_generator/example/lib/docs/animated_url_list/url.dart#L10

then this would fail
https://github.com/artflutter/reactive_forms_generator/blob/master/packages/reactive_forms_generator/example/lib/docs/animated_url_list/url.gform.dart#L444-L446

because the actual type is
https://github.com/artflutter/reactive_forms_generator/blob/master/packages/reactive_forms_generator/example/lib/docs/animated_url_list/url.gform.dart#L349-L350

Freezed: Generated *.gform.dart contains errors (Generated duplicated code)

Using Freezed. The generated model.gform.dart file contains errors. It generated duplicate code.

Files: GitHub Gist

Here's errors when run:

Terminal

lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:200:10: Error: 'jobControlPath' is already declared in this scope.
  String jobControlPath() => pathBuilder(jobControlName);
         ^^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:197:10: Context: Previous declaration of 'jobControlPath'.
  String jobControlPath() => pathBuilder(jobControlName);
         ^^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:204:35: Error: '_jobValue' is already declared in this scope.
  InboundOrderNewFormModelJob get _jobValue => jobForm.model;
                                  ^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:201:35: Context: Previous declaration of '_jobValue'.
  InboundOrderNewFormModelJob get _jobValue => jobForm.model;
                                  ^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:232:12: Error: 'containsJob' is already declared in this scope.
  bool get containsJob {
           ^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:205:12: Context: Previous declaration of 'containsJob'.
  bool get containsJob {
           ^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:244:15: Error: 'jobErrors' is already declared in this scope.
  Object? get jobErrors => jobControl.errors;
              ^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:241:15: Context: Previous declaration of 'jobErrors'.
  Object? get jobErrors => jobControl.errors;
              ^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:248:12: Error: 'jobFocus' is already declared in this scope.
  void get jobFocus => form.focus(jobControlPath());
           ^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:245:12: Context: Previous declaration of 'jobFocus'.
  void get jobFocus => form.focus(jobControlPath());
           ^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:280:8: Error: 'jobValueUpdate' is already declared in this scope.
  void jobValueUpdate(
       ^^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:249:8: Context: Previous declaration of 'jobValueUpdate'.
  void jobValueUpdate(
       ^^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:324:8: Error: 'jobValuePatch' is already declared in this scope.
  void jobValuePatch(
       ^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:293:8: Context: Previous declaration of 'jobValuePatch'.
  void jobValuePatch(
       ^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:368:8: Error: 'jobValueReset' is already declared in this scope.
  void jobValueReset(
       ^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:337:8: Context: Previous declaration of 'jobValueReset'.
  void jobValueReset(
       ^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:386:17: Error: 'jobControl' is already declared in this scope.
  FormGroup get jobControl => form.control(jobControlPath()) as FormGroup;
                ^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:381:17: Context: Previous declaration of 'jobControl'.
  FormGroup get jobControl => form.control(jobControlPath()) as FormGroup;
                ^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:441:8: Error: 'jobSetDisabled' is already declared in this scope.
  void jobSetDisabled(
       ^^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:387:8: Context: Previous declaration of 'jobSetDisabled'.
  void jobSetDisabled(
       ^^^^^^^^^^^^^^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:207:20: Error: Can't use 'jobControlPath' because it is declared more than once.
      form.control(jobControlPath());
                   ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:234:20: Error: Can't use 'jobControlPath' because it is declared more than once.
      form.control(jobControlPath());
                   ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:241:28: Error: Can't use 'jobControl' because it is declared more than once.
  Object? get jobErrors => jobControl.errors;
                           ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:244:28: Error: Can't use 'jobControl' because it is declared more than once.
  Object? get jobErrors => jobControl.errors;
                           ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:245:35: Error: Can't use 'jobControlPath' because it is declared more than once.
  void get jobFocus => form.focus(jobControlPath());
                                  ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:248:35: Error: Can't use 'jobControlPath' because it is declared more than once.
  void get jobFocus => form.focus(jobControlPath());
                                  ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:254:5: Error: Can't use 'jobControl' because it is declared more than once.
    jobControl.updateValue(
    ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:285:5: Error: Can't use 'jobControl' because it is declared more than once.
    jobControl.updateValue(
    ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:298:5: Error: Can't use 'jobControl' because it is declared more than once.
    jobControl.updateValue(
    ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:329:5: Error: Can't use 'jobControl' because it is declared more than once.
    jobControl.updateValue(
    ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:344:7: Error: Can't use 'jobControl' because it is declared more than once.
      jobControl.reset(
      ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:375:7: Error: Can't use 'jobControl' because it is declared more than once.
      jobControl.reset(
      ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:381:44: Error: Can't use 'jobControlPath' because it is declared more than once.
  FormGroup get jobControl => form.control(jobControlPath()) as FormGroup;
                                           ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:386:44: Error: Can't use 'jobControlPath' because it is declared more than once.
  FormGroup get jobControl => form.control(jobControlPath()) as FormGroup;
                                           ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:393:7: Error: Can't use 'jobControl' because it is declared more than once.
      jobControl.markAsDisabled(
      ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:398:7: Error: Can't use 'jobControl' because it is declared more than once.
      jobControl.markAsEnabled(
      ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:447:7: Error: Can't use 'jobControl' because it is declared more than once.
      jobControl.markAsDisabled(
      ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:452:7: Error: Can't use 'jobControl' because it is declared more than once.
      jobControl.markAsEnabled(
      ^
lib/src/features/home/inbound/order/new/domain/form/model.gform.dart:466:14: Error: Can't use '_jobValue' because it is declared more than once.
        job: _jobValue, importEntryNo: _importEntryNoValue, blNo: _blNoValue);
             ^

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.