From aa31ead75c1842e18fd61188739e64acb4a5e102 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 16 Oct 2019 21:09:01 -0400 Subject: [PATCH] NumField, DoubleField, IntField --- lib/src/common_fields.dart | 107 ++++++++++++++++++++++++++++++++++++- lib/src/field.dart | 10 ++-- lib/src/form.dart | 2 +- lib/src/form_renderer.dart | 2 + test/all_test.dart | 4 ++ 5 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 test/all_test.dart diff --git a/lib/src/common_fields.dart b/lib/src/common_fields.dart index b9c47c27..ef41bfa3 100644 --- a/lib/src/common_fields.dart +++ b/lib/src/common_fields.dart @@ -23,7 +23,7 @@ class TextField extends Field { renderer.visitTextField(this); @override - FutureOr> read( + FutureOr> read(RequestContext req, Map fields, Iterable files) { var value = fields[name] as String; if (trim) { @@ -50,7 +50,7 @@ class BoolField extends Field { renderer.visitBoolField(this); @override - FutureOr> read( + FutureOr> read(RequestContext req, Map fields, Iterable files) { if (fields.containsKey(name)) { return FieldReadResult.success(true); @@ -59,3 +59,106 @@ class BoolField extends Field { } } } + +/// A [Field] that parses its value as a [num]. +class NumField extends Field { + // Reuse text validation logic. + TextField _textField; + + /// The minimum/maximum value for the field. + final T min, max; + + /// The amount for a form field to increment by. + final num step; + + NumField(String name, + {String label, bool isRequired = true, this.max, this.min, this.step}) + : super(name, label: label, isRequired: isRequired) { + _textField = TextField(name, label: label, isRequired: isRequired); + } + + @override + FutureOr accept(FormRenderer renderer) => + renderer.visitNumField(this); + + @override + Future> read(RequestContext req, + Map fields, Iterable files) async { + var result = await _textField.read(req, fields, files); + if (result == null) { + return null; + } else if (result.isSuccess != true) { + return FieldReadResult.failure(result.errors); + } else { + var value = num.tryParse(result.value); + if (value != null) { + if (min != null && value < min) { + return FieldReadResult.failure(['"$name" can be no less than $min.']); + } else if (max != null && value > max) { + return FieldReadResult.failure( + ['"$name" can be no greater than $max.']); + } else { + return FieldReadResult.success(value as T); + } + } else { + return FieldReadResult.failure(['"$name" must be a number.']); + } + } + } +} + +/// A [NumField] that coerces its value to a [double]. +class DoubleField extends NumField { + DoubleField(String name, + {String label, bool isRequired = true, num step, double min, double max}) + : super(name, + label: label, + isRequired: isRequired, + step: step, + min: min, + max: max); + + @override + Future> read(RequestContext req, + Map fields, Iterable files) async { + var result = await super.read(req, fields, files); + if (result == null) { + return null; + } else if (!result.isSuccess) { + return FieldReadResult.failure(result.errors); + } else { + return FieldReadResult.success(result.value.toDouble()); + } + } +} + +/// A [NumField] that requires its value to be an [int]. +/// Passing a [double] will result in an error, so [step] defaults to 1. +class IntField extends NumField { + IntField(String name, + {String label, bool isRequired = true, num step = 1, int min, int max}) + : super(name, + label: label, + isRequired: isRequired, + step: step, + min: min, + max: max); + + @override + Future> read(RequestContext req, + Map fields, Iterable files) async { + var result = await super.read(req, fields, files); + if (result == null) { + return null; + } else if (!result.isSuccess) { + return FieldReadResult.failure(result.errors); + } else { + var value = result.value; + if (value is int) { + return FieldReadResult.success(result.value); + } else { + return FieldReadResult.failure(['"$name" must be an integer.']); + } + } + } +} diff --git a/lib/src/field.dart b/lib/src/field.dart index 3c6b302a..c788711b 100644 --- a/lib/src/field.dart +++ b/lib/src/field.dart @@ -44,7 +44,7 @@ abstract class Field { /// /// If it returns `null` and [isRequired] is `true`, an error must /// be generated. - FutureOr> read( + FutureOr> read(RequestContext req, Map fields, Iterable files); /// Accepts a form renderer. @@ -69,8 +69,8 @@ abstract class Field { await req.parseBody(); uploadedFiles = req.uploadedFiles; } - var result = - await read(query ? req.queryParameters : req.bodyAsMap, uploadedFiles); + var result = await read( + req, query ? req.queryParameters : req.bodyAsMap, uploadedFiles); if (result?.isSuccess != true && defaultValue != null) { return defaultValue; } else if (result == null) { @@ -99,9 +99,9 @@ class _MatchedField extends Field { FutureOr accept(FormRenderer renderer) => inner.accept(renderer); @override - Future> read( + Future> read(RequestContext req, Map fields, Iterable files) async { - var result = await inner.read(fields, files); + var result = await inner.read(req, fields, files); if (!result.isSuccess) { return result; } else { diff --git a/lib/src/form.dart b/lib/src/form.dart index c76ec7f8..de017afa 100644 --- a/lib/src/form.dart +++ b/lib/src/form.dart @@ -109,7 +109,7 @@ class Form { for (var field in fields) { var result = await field.read( - query ? req.queryParameters : req.bodyAsMap, uploadedFiles); + req, query ? req.queryParameters : req.bodyAsMap, uploadedFiles); if (result == null && field.isRequired) { errors.add(reportMissingField(field.name, query: query)); } else if (!result.isSuccess) { diff --git a/lib/src/form_renderer.dart b/lib/src/form_renderer.dart index 43ff919b..3e267328 100644 --- a/lib/src/form_renderer.dart +++ b/lib/src/form_renderer.dart @@ -9,5 +9,7 @@ abstract class FormRenderer { FutureOr visitBoolField(BoolField field); + FutureOr visitNumField(NumField field); + FutureOr visitTextField(TextField field); } diff --git a/test/all_test.dart b/test/all_test.dart new file mode 100644 index 00000000..6f4607e5 --- /dev/null +++ b/test/all_test.dart @@ -0,0 +1,4 @@ +import 'package:angel_validate/angel_validate.dart'; +import 'package:test/test.dart'; + +void main() {}