Field comments
This commit is contained in:
parent
b50102d611
commit
c090e9878e
4 changed files with 81 additions and 1 deletions
|
@ -1,3 +1,4 @@
|
|||
export 'src/common_fields.dart';
|
||||
export 'src/field.dart';
|
||||
export 'src/form.dart';
|
||||
export 'src/form_renderer.dart';
|
||||
|
|
50
lib/src/common_fields.dart
Normal file
50
lib/src/common_fields.dart
Normal file
|
@ -0,0 +1,50 @@
|
|||
import 'dart:async';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'field.dart';
|
||||
import 'form_renderer.dart';
|
||||
|
||||
/// A [Field] that accepts plain text.
|
||||
class TextField extends Field<String> {
|
||||
/// If `true`, then renderers will produce a `<textarea>` element.
|
||||
final bool isTextArea;
|
||||
|
||||
TextField(String name,
|
||||
{String label, bool isRequired = false, this.isTextArea = false})
|
||||
: super(name, label: label, isRequired: isRequired);
|
||||
|
||||
@override
|
||||
FutureOr<U> accept<U>(FormRenderer<U> renderer) =>
|
||||
renderer.visitTextField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<String>> read(RequestContext req) {
|
||||
var value = req.bodyAsMap[name] as String;
|
||||
if (value == null) {
|
||||
return null;
|
||||
} else {
|
||||
return FieldReadResult.success(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [Field] that checks simply for its presence in the given data.
|
||||
/// Typically used for checkboxes.
|
||||
class BoolField extends Field<bool> {
|
||||
BoolField(String name, {String label, bool isRequired = false})
|
||||
: super(name, label: label, isRequired: isRequired);
|
||||
|
||||
@override
|
||||
FutureOr<U> accept<U>(FormRenderer<U> renderer) =>
|
||||
renderer.visitBoolField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<bool>> read(RequestContext req) {
|
||||
if (req.bodyAsMap.containsKey(name)) {
|
||||
return FieldReadResult.success(true);
|
||||
} else if (!isRequired) {
|
||||
return FieldReadResult.success(false);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,31 +3,53 @@ import 'package:angel_framework/angel_framework.dart';
|
|||
import 'package:matcher/matcher.dart';
|
||||
import 'form_renderer.dart';
|
||||
|
||||
/// Holds the result of validating a field.
|
||||
class FieldReadResult<T> {
|
||||
/// If `true`, then validation was successful.
|
||||
/// If `false`, [errors] must not be empty.
|
||||
final bool isSuccess;
|
||||
|
||||
/// The value provided by the user.
|
||||
final T value;
|
||||
|
||||
/// Any errors that arose during validation.
|
||||
final Iterable<String> errors;
|
||||
|
||||
FieldReadResult.success(this.value)
|
||||
: isSuccess = true,
|
||||
errors = null;
|
||||
errors = [];
|
||||
|
||||
FieldReadResult.failure(this.errors)
|
||||
: isSuccess = false,
|
||||
value = null;
|
||||
}
|
||||
|
||||
/// An abstraction used to fetch values from request bodies, in a type-safe manner.
|
||||
abstract class Field<T> {
|
||||
/// The name of this field. This is the name that users should include in
|
||||
/// request bodies.
|
||||
final String name;
|
||||
|
||||
/// An optional label for the field.
|
||||
final String label;
|
||||
|
||||
/// Whether the field is required. If `true`, then if it is not
|
||||
/// present, an error will be generated.
|
||||
final bool isRequired;
|
||||
|
||||
Field(this.name, {this.label, this.isRequired = false});
|
||||
|
||||
/// Reads the value from the request body.
|
||||
///
|
||||
/// If it returns `null` and [isRequired] is `true`, an error must
|
||||
/// be generated.
|
||||
FutureOr<FieldReadResult<T>> read(RequestContext req);
|
||||
|
||||
/// Accepts a form renderer.
|
||||
FutureOr<U> accept<U>(FormRenderer<U> renderer);
|
||||
|
||||
/// Wraps this instance in one that throws an error if any of the
|
||||
/// [matchers] fails.
|
||||
Field<T> match(Iterable<Matcher> matchers) => _MatchedField(this, matchers);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import 'dart:async';
|
||||
import 'common_fields.dart';
|
||||
import 'field.dart';
|
||||
|
||||
abstract class FormRenderer<T> {
|
||||
const FormRenderer();
|
||||
|
||||
FutureOr<T> visit(Field<T> field) => field.accept(this);
|
||||
|
||||
FutureOr<T> visitBoolField(BoolField field);
|
||||
|
||||
FutureOr<T> visitTextField(TextField field);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue