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);
|
renderer.visitTextField(this);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<FieldReadResult<String>> read(
|
FutureOr<FieldReadResult<String>> read(RequestContext req,
|
||||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||||
var value = fields[name] as String;
|
var value = fields[name] as String;
|
||||||
if (trim) {
|
if (trim) {
|
||||||
|
@ -50,7 +50,7 @@ class BoolField extends Field<bool> {
|
||||||
renderer.visitBoolField(this);
|
renderer.visitBoolField(this);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<FieldReadResult<bool>> read(
|
FutureOr<FieldReadResult<bool>> read(RequestContext req,
|
||||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||||
if (fields.containsKey(name)) {
|
if (fields.containsKey(name)) {
|
||||||
return FieldReadResult.success(true);
|
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
|
/// If it returns `null` and [isRequired] is `true`, an error must
|
||||||
/// be generated.
|
/// be generated.
|
||||||
FutureOr<FieldReadResult<T>> read(
|
FutureOr<FieldReadResult<T>> read(RequestContext req,
|
||||||
Map<String, dynamic> fields, Iterable<UploadedFile> files);
|
Map<String, dynamic> fields, Iterable<UploadedFile> files);
|
||||||
|
|
||||||
/// Accepts a form renderer.
|
/// Accepts a form renderer.
|
||||||
|
@ -69,8 +69,8 @@ abstract class Field<T> {
|
||||||
await req.parseBody();
|
await req.parseBody();
|
||||||
uploadedFiles = req.uploadedFiles;
|
uploadedFiles = req.uploadedFiles;
|
||||||
}
|
}
|
||||||
var result =
|
var result = await read(
|
||||||
await read(query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
req, query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
||||||
if (result?.isSuccess != true && defaultValue != null) {
|
if (result?.isSuccess != true && defaultValue != null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
} else if (result == null) {
|
} else if (result == null) {
|
||||||
|
@ -99,9 +99,9 @@ class _MatchedField<T> extends Field<T> {
|
||||||
FutureOr<U> accept<U>(FormRenderer<U> renderer) => inner.accept(renderer);
|
FutureOr<U> accept<U>(FormRenderer<U> renderer) => inner.accept(renderer);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<FieldReadResult<T>> read(
|
Future<FieldReadResult<T>> read(RequestContext req,
|
||||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
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) {
|
if (!result.isSuccess) {
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -109,7 +109,7 @@ class Form {
|
||||||
|
|
||||||
for (var field in fields) {
|
for (var field in fields) {
|
||||||
var result = await field.read(
|
var result = await field.read(
|
||||||
query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
req, query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
||||||
if (result == null && field.isRequired) {
|
if (result == null && field.isRequired) {
|
||||||
errors.add(reportMissingField(field.name, query: query));
|
errors.add(reportMissingField(field.name, query: query));
|
||||||
} else if (!result.isSuccess) {
|
} else if (!result.isSuccess) {
|
||||||
|
|
|
@ -9,5 +9,7 @@ abstract class FormRenderer<T> {
|
||||||
|
|
||||||
FutureOr<T> visitBoolField(BoolField field);
|
FutureOr<T> visitBoolField(BoolField field);
|
||||||
|
|
||||||
|
FutureOr<T> visitNumField(NumField field);
|
||||||
|
|
||||||
FutureOr<T> visitTextField(TextField 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