在表单字段模型中存储/翻译静态错误字符串是不好的做法吗?

发布于 2025-01-22 14:10:21 字数 1811 浏览 0 评论 0原文

我正在构建一个Flutter应用程序,其中有一个注册表格,如果用户输入无效的电子邮件,将会出现错误。

我有以下电子邮件表单字段的模型(使用 formz package):

enum EmailFieldValidationError { empty, invalid }

class EmailField extends FormzInput<String, EmailFieldValidationError> {
  const EmailField.pure() : super.pure('');

  const EmailField.dirty([String value = '']) : super.dirty(value);

  static final RegExp _emailRegExp = RegExp(
    r'^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$',
  );

  @override
  EmailFieldValidationError? validator(String? value) {
  }

  String? get errorText {
    if (error != null && !pure && value.isNotEmpty) {
      switch (error) {
        case EmailFieldValidationError.invalid:
          return 'Invalid email.';
        default:
          return null;
      }
    }
    return null;
  }
}

这使事情变得非常简单。现在,当我想在textformfield()下有条件地显示错误消息时,现在在我的小部件中,但是在我看来,这就像在考虑本地化(我想稍后再添加到应用程序)中的不良练习中幻影将要求我将buildContext用于 build 而不是错误字符串。像这样:

  String? buildErrorText(BuildContext context) {
    if (error != null && !pure && value.isNotEmpty) {
      switch (error) {
        case EmailFieldValidationError.invalid:
          return AppLocalizations.of(context)!.emailFieldInvalidValueError;
        default:
          return null;
      }
    }
    return null;
  }

鉴于我需要实例化buildContext并在我的本地化类中进行配置,以便能够测试此表单字段模型,因此我认为这里关注点的分离很差。

我认为最初为我的表单本身中的表单字段模型生成错误文本是一个好主意。这主要是为了避免重复代码。但是,在本地化成为我的应用程序的不错之后,这成为一个问题,我遇到分析瘫痪阅读/考虑从模型类中静态字符串生成的不同选项,同时避免我的原始重复代码问题。

在这里,任何想法都将不胜感激。

I'm building a Flutter app where I have a sign-up form that will throw an error if the user inputs an invalid email.

I have the following model for the email form field (using formz package):

enum EmailFieldValidationError { empty, invalid }

class EmailField extends FormzInput<String, EmailFieldValidationError> {
  const EmailField.pure() : super.pure('');

  const EmailField.dirty([String value = '']) : super.dirty(value);

  static final RegExp _emailRegExp = RegExp(
    r'^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*

This makes things very simple for now within my widgets when I want to conditionally show error messages under a TextFormField(), but this seems to me like bad practice when considering how localization (which I would like to add to the app later) in flutter would require me to use the BuildContext to build the error strings instead. Like so:

  String? buildErrorText(BuildContext context) {
    if (error != null && !pure && value.isNotEmpty) {
      switch (error) {
        case EmailFieldValidationError.invalid:
          return AppLocalizations.of(context)!.emailFieldInvalidValueError;
        default:
          return null;
      }
    }
    return null;
  }

Given that I'd need to instantiate a BuildContext and configure it with my localization class just to be able to test this form field model, I think that there is poor separation of concerns here.

I thought it would be a good idea at first to generate error texts for my form field models within the class itself. This was primarily to avoid repeated code. But after localization became a nice-to-have for my app, this became a problem, and I'm running into analysis paralysis reading/considering different options for spearating the static string generation from the model class while avoiding my original problem of repeated code.

Any ideas would be much appreciated here.

, ); @override EmailFieldValidationError? validator(String? value) { } String? get errorText { if (error != null && !pure && value.isNotEmpty) { switch (error) { case EmailFieldValidationError.invalid: return 'Invalid email.'; default: return null; } } return null; } }

This makes things very simple for now within my widgets when I want to conditionally show error messages under a TextFormField(), but this seems to me like bad practice when considering how localization (which I would like to add to the app later) in flutter would require me to use the BuildContext to build the error strings instead. Like so:

Given that I'd need to instantiate a BuildContext and configure it with my localization class just to be able to test this form field model, I think that there is poor separation of concerns here.

I thought it would be a good idea at first to generate error texts for my form field models within the class itself. This was primarily to avoid repeated code. But after localization became a nice-to-have for my app, this became a problem, and I'm running into analysis paralysis reading/considering different options for spearating the static string generation from the model class while avoiding my original problem of repeated code.

Any ideas would be much appreciated here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

月依秋水 2025-01-29 14:10:21

我最终制定了一个简单的解决方案,并且由于我没有得到任何可行的答案,所以这是我的解决方案。如果有人能够验证或提供反馈以进一步改善它,那就太好了。

我基本上创建了一个新的“通用”小部件,以简单地基于给定的emailfield值渲染textformfield。如果当前值有任何错误,它将基于错误类型渲染适当的错误字符串:

class CommonEmailFormField extends StatelessWidget {
  const CommonEmailFormField({
    Key? key,
    required this.email,
    this.onChanged,
  }) : super(key: key);

  final EmailField email;
  final Function(String)? onChanged;

  String? get _errorText {
    if (email.error != null && !email.pure && email.value.isNotEmpty) {
      switch (email.error) {
        case EmailFieldValidationError.invalid:
          return 'Invalid email.';
        default:
          return null;
      }
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      initialValue: email.value,
      onChanged: onChanged,
      decoration: InputDecoration(
        labelText: 'Email *',
        errorText: _errorText,
      ),
    );
  }
}

我将摘要简化为简洁,并且要点。您可以查看实际实现在这里

一旦我开始添加本地化,​​它将需要我更改_errortext getter从buildcontext getter读取静态局部化字符串,但这仍然是代码驱动UI行为并保持分离的代码关注。

无论如何,该解决方案解决了我的主要问题:

  1. 不必要的测试复杂性。现在,我不需要设置UI buildContext就可以在添加本地化后能够测试字段模型。
  2. 重复代码。我可以在其他地方重复使用这个常见的小部件。
  3. 关注点的分离。专门驱动UI/视图行为的代码与驱动模型行为的代码很好地分开。此弯曲简化了测试。

I worked out a simple solution eventually and since I didn't get any viable answers, here is my solution. It would be nice if anyone could validate or give feedback to further improve it.

I basically created a new "common" widget to simply render a TextFormField based on the given EmailField value. If the current value has any errors, it will render an appropriate error string based on the error type:

class CommonEmailFormField extends StatelessWidget {
  const CommonEmailFormField({
    Key? key,
    required this.email,
    this.onChanged,
  }) : super(key: key);

  final EmailField email;
  final Function(String)? onChanged;

  String? get _errorText {
    if (email.error != null && !email.pure && email.value.isNotEmpty) {
      switch (email.error) {
        case EmailFieldValidationError.invalid:
          return 'Invalid email.';
        default:
          return null;
      }
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      initialValue: email.value,
      onChanged: onChanged,
      decoration: InputDecoration(
        labelText: 'Email *',
        errorText: _errorText,
      ),
    );
  }
}

I've simplified the snippet to be concise and to the point. You can take a look at the actual implementation here.

Once I start adding localization, it will require me to change the _errorText getter read the static localized strings from the BuildContext, but that's still code that drives UI behavior and I maintain separation of concerns.

Regardless, this solution solves my main problems of:

  1. Unnecessary complexity in testing. Now I don't need to set up a UI BuildContext to be able to test field models once I add localization.
  2. Repeated code. I can just reuse this common widget wherever else I need.
  3. Separation of concerns. Code that drives specifically UI/View behavior is nicely separated from code that drives Model behavior. This in-turn simplifies testing.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文