validate: Add MapField, Field.deserialize, Field.decode
This commit is contained in:
parent
6338abd79e
commit
1ed5d20ad6
3 changed files with 48 additions and 8 deletions
|
@ -72,6 +72,14 @@ Future<void> main() async {
|
|||
</html>
|
||||
''');
|
||||
});
|
||||
|
||||
app.post('/b', (req, res) async {
|
||||
var form = Form(fields: [
|
||||
MapField('todo', todoForm),
|
||||
]);
|
||||
var data = await form.read(req);
|
||||
return data;
|
||||
});
|
||||
|
||||
app.fallback((req, res) => throw AngelHttpException.notFound());
|
||||
|
||||
|
@ -82,7 +90,7 @@ Future<void> main() async {
|
|||
}
|
||||
};
|
||||
|
||||
await http.startServer('127.0.0.1', 3000);
|
||||
await http.startServer('127.0.0.1', 3011);
|
||||
print('Listening at ${http.uri}');
|
||||
}
|
||||
|
||||
|
|
|
@ -353,12 +353,12 @@ class ImageField extends Field<Image> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A [Field] implementation that reads structured data by decoding it with a [Form].
|
||||
class MapField extends Field<Map<String, dynamic>> {
|
||||
@override
|
||||
final String name;
|
||||
/// The underlying [Form] that specifies the types of data this object must contained.
|
||||
final Form form;
|
||||
|
||||
MapField(this.name, this.form, {String label, bool isRequired = true})
|
||||
MapField(String name, this.form, {String label, bool isRequired = true})
|
||||
: super(name, 'text', label: label, isRequired: isRequired);
|
||||
|
||||
@override
|
||||
|
@ -369,17 +369,17 @@ class MapField extends Field<Map<String, dynamic>> {
|
|||
FutureOr<FieldReadResult<Map<String, dynamic>>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||
var value = fields[name];
|
||||
Map mapValue;
|
||||
Map<String, dynamic> mapValue;
|
||||
|
||||
// Value can be either a Map, or a JSON-encoded Map.
|
||||
if (value == null) {
|
||||
return null;
|
||||
} else if (value is Map) {
|
||||
mapValue = value;
|
||||
mapValue = value.cast();
|
||||
} else if (value is String) {
|
||||
var decoded = json.decode(value);
|
||||
if (decoded is Map) {
|
||||
mapValue = decoded;
|
||||
mapValue = decoded.cast();
|
||||
} else {
|
||||
return FieldReadResult.failure([
|
||||
'If "$name" is given as a string, then it must produce a map/dict/object when decoded as JSON.'
|
||||
|
@ -390,6 +390,6 @@ class MapField extends Field<Map<String, dynamic>> {
|
|||
['"$name" must be either a map/dict/object, or a string.']);
|
||||
}
|
||||
|
||||
|
||||
return form.readFromMap(mapValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:matcher/matcher.dart';
|
||||
import 'form.dart';
|
||||
|
@ -57,6 +58,14 @@ abstract class Field<T> {
|
|||
/// [matchers] fails.
|
||||
Field<T> match(Iterable<Matcher> matchers) => _MatchedField(this, matchers);
|
||||
|
||||
/// Wraps this instance in one that calls the [converter] to deserialize
|
||||
/// the value into another type.
|
||||
Field<U> deserialize<U>(FutureOr<U> Function(T) converter) =>
|
||||
_DeserializeField(this, converter);
|
||||
|
||||
/// Same as [deserialize], but uses a [codec] to deserialize data.
|
||||
Field<U> decode<U>(Codec<U, T> codec) => deserialize(codec.decode);
|
||||
|
||||
/// Calls [read], and returns the retrieve value from the body.
|
||||
///
|
||||
/// If [query] is `true` (default: `false`), then the value will
|
||||
|
@ -124,3 +133,26 @@ class _MatchedField<T> extends Field<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _DeserializeField<T, U> extends Field<U> {
|
||||
final Field<T> inner;
|
||||
final FutureOr<U> Function(T) converter;
|
||||
|
||||
_DeserializeField(this.inner, this.converter)
|
||||
: super(inner.name, inner.type,
|
||||
label: inner.label, isRequired: inner.isRequired);
|
||||
|
||||
@override
|
||||
FutureOr<X> accept<X>(FormRenderer<X> renderer) => inner.accept(renderer);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<U>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await inner.read(fields, files);
|
||||
if (!result.isSuccess) {
|
||||
return FieldReadResult.failure(result.errors);
|
||||
} else {
|
||||
return FieldReadResult.success(await converter(result.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue