Field comments

This commit is contained in:
Tobe O 2019-10-16 20:26:48 -04:00
parent b50102d611
commit c090e9878e
4 changed files with 81 additions and 1 deletions

View file

@ -1,3 +1,4 @@
export 'src/common_fields.dart';
export 'src/field.dart';
export 'src/form.dart';
export 'src/form_renderer.dart';

View 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;
}
}
}

View file

@ -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);
}

View file

@ -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);
}