NumField, DoubleField, IntField
This commit is contained in:
parent
b3e2e8a401
commit
aa31ead75c
5 changed files with 117 additions and 8 deletions
|
@ -23,7 +23,7 @@ class TextField extends Field<String> {
|
|||
renderer.visitTextField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<String>> read(
|
||||
FutureOr<FieldReadResult<String>> read(RequestContext req,
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||
var value = fields[name] as String;
|
||||
if (trim) {
|
||||
|
@ -50,7 +50,7 @@ class BoolField extends Field<bool> {
|
|||
renderer.visitBoolField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<bool>> read(
|
||||
FutureOr<FieldReadResult<bool>> read(RequestContext req,
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||
if (fields.containsKey(name)) {
|
||||
return FieldReadResult.success(true);
|
||||
|
@ -59,3 +59,106 @@ class BoolField extends Field<bool> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [Field] that parses its value as a [num].
|
||||
class NumField<T extends num> extends Field<T> {
|
||||
// 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<U> accept<U>(FormRenderer<U> renderer) =>
|
||||
renderer.visitNumField(this);
|
||||
|
||||
@override
|
||||
Future<FieldReadResult<T>> read(RequestContext req,
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> 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<double> {
|
||||
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<FieldReadResult<double>> read(RequestContext req,
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> 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<int> {
|
||||
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<FieldReadResult<int>> read(RequestContext req,
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> 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.']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ abstract class Field<T> {
|
|||
///
|
||||
/// If it returns `null` and [isRequired] is `true`, an error must
|
||||
/// be generated.
|
||||
FutureOr<FieldReadResult<T>> read(
|
||||
FutureOr<FieldReadResult<T>> read(RequestContext req,
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files);
|
||||
|
||||
/// Accepts a form renderer.
|
||||
|
@ -69,8 +69,8 @@ abstract class Field<T> {
|
|||
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<T> extends Field<T> {
|
|||
FutureOr<U> accept<U>(FormRenderer<U> renderer) => inner.accept(renderer);
|
||||
|
||||
@override
|
||||
Future<FieldReadResult<T>> read(
|
||||
Future<FieldReadResult<T>> read(RequestContext req,
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await inner.read(fields, files);
|
||||
var result = await inner.read(req, fields, files);
|
||||
if (!result.isSuccess) {
|
||||
return result;
|
||||
} else {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -9,5 +9,7 @@ abstract class FormRenderer<T> {
|
|||
|
||||
FutureOr<T> visitBoolField(BoolField field);
|
||||
|
||||
FutureOr<T> visitNumField(NumField field);
|
||||
|
||||
FutureOr<T> visitTextField(TextField field);
|
||||
}
|
||||
|
|
4
test/all_test.dart
Normal file
4
test/all_test.dart
Normal file
|
@ -0,0 +1,4 @@
|
|||
import 'package:angel_validate/angel_validate.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {}
|
Loading…
Reference in a new issue