validate: update README for MapField

This commit is contained in:
Tobe O 2020-05-04 14:35:16 -04:00
parent 1ed5d20ad6
commit 5b3472c677
3 changed files with 48 additions and 15 deletions

View file

@ -28,15 +28,30 @@ its usage in `package:test`):
var positiveNumberField = IntField('pos_num').match([isPositive]); var positiveNumberField = IntField('pos_num').match([isPositive]);
``` ```
A `MapField` can embed a `Form` (forms covered below), and when combined with
`Field.deserialize`, can be used to deserialize structured data as a body field:
```dart
app.post('/map_field', (req, res) async {
var form = Form(fields: [
MapField('todo', todoForm).deserialize(Todo.fromMap),
]);
var data = await form.validate(req);
print(data['todo'] is Todo);
});
```
There are several included field types: There are several included field types:
* `TextField` * `TextField` - Standard text input.
* `BoolField` * `BoolField` - Checks if a field is present; used for checkboxes.
* `NumField` * `NumField` - Base class that parses input as a number.
* `DoubleField` * `DoubleField` - Specialization of `NumField` for doubles.
* `IntField` * `IntField` - Specialization of `NumField` for integers.
* `DateTimeField` * `DateTimeField` - Parses an input as an ISO-8601 date.
* `FileField` * `FileField` - Validates a file in `req.uploadedFiles`.
* `ImageField` * `ImageField` - Uses `package:image` to decode an `UploadedFile` into an image.
* `MapField` - Validates a Map using a Form.
# Forms # Forms
The `Form` class lets you combine `Field` instances, and decode The `Form` class lets you combine `Field` instances, and decode
@ -55,9 +70,15 @@ var todo = await todoForm.deserialize(req, TodoSerializer.fromMap);
// Same as above, but with a Codec<Todo, Map> (i.e. via `angel_serialize`). // Same as above, but with a Codec<Todo, Map> (i.e. via `angel_serialize`).
var todo = await todoForm.decode(req, todoSerializer); var todo = await todoForm.decode(req, todoSerializer);
// Same as above, but returns the plain Map without any deserialization.
var todoMap = await todoForm.validate(req);
// Lower-level functionality, typically not called directly. // Lower-level functionality, typically not called directly.
// Use it if you want to handle validation errors directly, without // Use it if you want to handle validation errors directly, without
// throwing exceptions. // throwing exceptions.
var result = await todoForm.read(req);
print(result.isSuccess);
print(result.errors.length);
@serializable @serializable
class _Todo { class _Todo {

View file

@ -73,12 +73,17 @@ Future<void> main() async {
'''); ''');
}); });
app.post('/b', (req, res) async { // You can use a [MapField] to embed one [Form] with another.
// In this example, we embed the [todoForm], but also call `.deserialize`,
// so that the final value we see is a [Todo] instance, rather than a [Map].
app.post('/map_field', (req, res) async {
var form = Form(fields: [ var form = Form(fields: [
MapField('todo', todoForm), MapField('todo', todoForm).deserialize(Todo.fromMap),
]); ]);
var data = await form.read(req); var data = await form.validate(req);
return data; var b = StringBuffer();
b.write(data);
return b.toString();
}); });
app.fallback((req, res) => throw AngelHttpException.notFound()); app.fallback((req, res) => throw AngelHttpException.notFound());
@ -103,4 +108,7 @@ class Todo {
static Todo fromMap(Map map) { static Todo fromMap(Map map) {
return Todo(map['text'] as String, map['is_complete'] as bool); return Todo(map['text'] as String, map['is_complete'] as bool);
} }
@override
String toString() => 'Todo($text, $isComplete)';
} }

View file

@ -115,7 +115,9 @@ class _MatchedField<T> extends Field<T> {
Future<FieldReadResult<T>> read( Future<FieldReadResult<T>> read(
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(fields, files);
if (!result.isSuccess) { if (result == null) {
return null;
} else if (!result.isSuccess) {
return result; return result;
} else { } else {
var errors = <String>[]; var errors = <String>[];
@ -149,7 +151,9 @@ class _DeserializeField<T, U> extends Field<U> {
FutureOr<FieldReadResult<U>> read( FutureOr<FieldReadResult<U>> read(
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(fields, files);
if (!result.isSuccess) { if (result == null) {
return null;
} else if (!result.isSuccess) {
return FieldReadResult.failure(result.errors); return FieldReadResult.failure(result.errors);
} else { } else {
return FieldReadResult.success(await converter(result.value)); return FieldReadResult.success(await converter(result.value));