validate: update README for MapField
This commit is contained in:
parent
1ed5d20ad6
commit
5b3472c677
3 changed files with 48 additions and 15 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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)';
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Loading…
Reference in a new issue