feat!(form): add repository feature, and controller for inputs
This commit is contained in:
parent
b89ef3de8a
commit
7a056ac38e
@ -32,3 +32,4 @@ export 'input_validators/name.dart';
|
||||
export 'input_validators/password.dart';
|
||||
export 'input_validators/phone.dart';
|
||||
export 'input_validators/text_string.dart';
|
||||
export 'repositories/form_repository_impl.dart';
|
||||
|
@ -30,6 +30,7 @@ class WyattFormImpl extends WyattForm {
|
||||
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
|
||||
dynamic>> _inputs;
|
||||
final FormValidator _validator;
|
||||
final String _name;
|
||||
|
||||
late List<
|
||||
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
|
||||
@ -37,8 +38,10 @@ class WyattFormImpl extends WyattForm {
|
||||
|
||||
WyattFormImpl(
|
||||
this._inputs, {
|
||||
required String name,
|
||||
FormValidator validationStrategy = const EveryInputValidator(),
|
||||
}) : _validator = validationStrategy {
|
||||
}) : _name = name,
|
||||
_validator = validationStrategy {
|
||||
_inputsInitial = _inputs.map((input) => input.clone()).toList();
|
||||
}
|
||||
|
||||
@ -50,6 +53,9 @@ class WyattFormImpl extends WyattForm {
|
||||
@override
|
||||
FormValidator get formValidationStrategy => _validator;
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
|
||||
@override
|
||||
bool containsKey(String key) => inputs.any((input) => input.key == key);
|
||||
|
||||
@ -94,22 +100,24 @@ class WyattFormImpl extends WyattForm {
|
||||
FormStatus validate() => formValidationStrategy.validate(this);
|
||||
|
||||
@override
|
||||
WyattForm clone() => WyattFormImpl(
|
||||
WyattForm clone() {
|
||||
final clone = WyattFormImpl(
|
||||
_inputs.map((input) => input.clone()).toList(),
|
||||
name: _name,
|
||||
validationStrategy: formValidationStrategy,
|
||||
);
|
||||
).._inputsInitial = _inputsInitial;
|
||||
return clone;
|
||||
}
|
||||
|
||||
@override
|
||||
WyattForm reset() => WyattFormImpl(
|
||||
WyattForm reset() {
|
||||
final newForm = WyattFormImpl(
|
||||
_inputsInitial,
|
||||
name: _name,
|
||||
validationStrategy: formValidationStrategy,
|
||||
);
|
||||
|
||||
@override
|
||||
bool? get stringify => true;
|
||||
|
||||
@override
|
||||
List<Object?> get props => _inputs;
|
||||
).._inputsInitial = _inputsInitial;
|
||||
return newForm;
|
||||
}
|
||||
|
||||
@override
|
||||
void updateMetadata<Extra>(String key, FormInputMetadata<Extra> metadata) {
|
||||
@ -133,4 +141,11 @@ class WyattFormImpl extends WyattForm {
|
||||
@override
|
||||
WyattForm operationWith(FormOperation operation, WyattForm other) =>
|
||||
operation.call(this, other);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [_inputs, _name, _validator];
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'WyattForm(name: $name, validation: ${_validator.runtimeType}, inputs: $inputs)';
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class FormDifference extends FormOperation {
|
||||
|
||||
return WyattFormImpl(
|
||||
inputs,
|
||||
name: a.name,
|
||||
validationStrategy: a.formValidationStrategy,
|
||||
);
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ class FormIntersection extends FormOperation {
|
||||
|
||||
return WyattFormImpl(
|
||||
inputs,
|
||||
name: a.name,
|
||||
validationStrategy: a.formValidationStrategy,
|
||||
);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ class FormUnion extends FormOperation {
|
||||
|
||||
return WyattFormImpl(
|
||||
inputs,
|
||||
name: a.name,
|
||||
validationStrategy: a.formValidationStrategy,
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
|
||||
import 'package:wyatt_form_bloc/src/domain/repositories/form_repository.dart';
|
||||
|
||||
class FormRepositoryImpl extends FormRepository {
|
||||
final Map<String, WyattForm> _runtimeForms = {};
|
||||
|
||||
@override
|
||||
Map<String, WyattForm> get runtimeForms => _runtimeForms;
|
||||
|
||||
@override
|
||||
WyattForm accessForm(String formName) {
|
||||
if (_runtimeForms.containsKey(formName)) {
|
||||
return _runtimeForms[formName]!;
|
||||
} else {
|
||||
throw ClientException(
|
||||
'Form $formName is not registered. Just use '
|
||||
'`FormRepository.register(yourForm);` before any operation on it.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void registerForm(WyattForm form) {
|
||||
_runtimeForms[form.name] = form;
|
||||
}
|
||||
|
||||
@override
|
||||
void updateForm(WyattForm form) {
|
||||
if (_runtimeForms.containsKey(form.name)) {
|
||||
_runtimeForms[form.name] = form;
|
||||
} else {
|
||||
throw ClientException(
|
||||
'Form ${form.name} is not registered. Just use '
|
||||
'`FormRepository.register(yourForm);` before any operation on it.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void unregisterForm(String formName) {
|
||||
_runtimeForms.remove(formName);
|
||||
}
|
||||
}
|
@ -21,3 +21,4 @@ export 'form_encoders/form_encoder.dart';
|
||||
export 'form_operations/form_operation.dart';
|
||||
export 'form_validators/form_validator.dart';
|
||||
export 'input_validators/form_input_validator.dart';
|
||||
export 'repositories/form_repository.dart';
|
||||
|
@ -59,8 +59,9 @@ class FormInput<
|
||||
);
|
||||
|
||||
@override
|
||||
bool? get stringify => true;
|
||||
List<Object?> get props => [key, validator, metadata];
|
||||
|
||||
@override
|
||||
List<Object?> get props => [key, validator, metadata];
|
||||
String toString() =>
|
||||
'FormInput(name: $name, value: ${validator.value}, status: ${validator.status.name}';
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator
|
||||
abstract class WyattForm extends Equatable {
|
||||
List<FormInput> get inputs;
|
||||
FormValidator get formValidationStrategy;
|
||||
String get name;
|
||||
|
||||
bool containsKey(String key);
|
||||
|
||||
|
@ -19,5 +19,6 @@ import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
|
||||
// ignore: one_member_abstracts
|
||||
abstract class FormOperation {
|
||||
const FormOperation();
|
||||
// TODO(hpcl): handle operation on `initialInputs`
|
||||
WyattForm call(WyattForm a, WyattForm b);
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:wyatt_form_bloc/src/core/enums/form_input_status.dart';
|
||||
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
|
||||
|
||||
part 'any_validator.dart';
|
||||
part 'equality_validator.dart';
|
||||
part 'nullable_validator.dart';
|
||||
part 'regex_validator.dart';
|
||||
part 'text_validator.dart';
|
||||
part 'nullable_validator.dart';
|
||||
part 'equality_validator.dart';
|
||||
part 'any_validator.dart';
|
||||
|
||||
/// {@template form_input_validator}
|
||||
/// A [FormInputValidator] represents the value of a single form input field.
|
||||
|
@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
abstract class FormRepository extends BaseRepository {
|
||||
Map<String, WyattForm> get runtimeForms;
|
||||
|
||||
void registerForm(WyattForm form);
|
||||
void updateForm(WyattForm form);
|
||||
WyattForm accessForm(String formName);
|
||||
void unregisterForm(String formName);
|
||||
}
|
@ -29,6 +29,8 @@ part 'form_data_state.dart';
|
||||
abstract class FormDataCubit<State extends FormDataState> extends Cubit<State> {
|
||||
FormDataCubit(super.initialState) : super();
|
||||
|
||||
String get formName;
|
||||
|
||||
FutureOr<void> dataChanged<Value>(
|
||||
String key,
|
||||
FormInputValidator<Value, ValidationError> dirtyValue,
|
||||
|
@ -21,22 +21,31 @@ import 'package:wyatt_form_bloc/src/core/enums/set_operations.dart';
|
||||
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
|
||||
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
|
||||
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
|
||||
import 'package:wyatt_form_bloc/src/domain/repositories/form_repository.dart';
|
||||
import 'package:wyatt_form_bloc/src/presentation/features/form_data/form_data_cubit.dart';
|
||||
|
||||
part 'form_data_state_impl.dart';
|
||||
|
||||
abstract class FormDataCubitImpl extends FormDataCubit<FormDataStateImpl> {
|
||||
FormDataCubitImpl(WyattForm form) : super(FormDataStateImpl(form: form));
|
||||
final FormRepository _formRepository;
|
||||
final String _formName;
|
||||
|
||||
FormDataCubitImpl(this._formRepository, this._formName)
|
||||
: super(FormDataStateImpl(form: _formRepository.accessForm(_formName)));
|
||||
|
||||
@override
|
||||
String get formName => _formName;
|
||||
|
||||
@override
|
||||
FutureOr<void> dataChanged<Value>(
|
||||
String key,
|
||||
FormInputValidator<Value, ValidationError> dirtyValue,
|
||||
) {
|
||||
final form = state.form.clone();
|
||||
final form = _formRepository.accessForm(_formName).clone();
|
||||
|
||||
try {
|
||||
form.updateValidator(key, dirtyValue);
|
||||
_formRepository.updateForm(form);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
@ -54,7 +63,9 @@ abstract class FormDataCubitImpl extends FormDataCubit<FormDataStateImpl> {
|
||||
WyattForm form, {
|
||||
SetOperation operation = SetOperation.replace,
|
||||
}) {
|
||||
final WyattForm newForm = operation.operation.call(state.form, form);
|
||||
final WyattForm current = _formRepository.accessForm(_formName).clone();
|
||||
final WyattForm newForm = operation.operation.call(current, form);
|
||||
_formRepository.updateForm(newForm);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
@ -67,6 +78,7 @@ abstract class FormDataCubitImpl extends FormDataCubit<FormDataStateImpl> {
|
||||
@override
|
||||
FutureOr<void> reset() {
|
||||
final form = state.form.reset();
|
||||
_formRepository.updateForm(form);
|
||||
emit(
|
||||
state.copyWith(
|
||||
form: form,
|
||||
|
@ -35,8 +35,9 @@ class FormDataStateImpl extends FormDataState {
|
||||
);
|
||||
|
||||
@override
|
||||
bool? get stringify => true;
|
||||
List<Object?> get props => [status, form, errorMessage];
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, form, errorMessage];
|
||||
String toString() =>
|
||||
'FormDataSate(status: ${status.name} ${(errorMessage != null) ? " [$errorMessage]" : ""}, $form';
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class InputBuilder<Cubit extends FormDataCubit> extends StatelessWidget {
|
||||
Widget build(BuildContext context) =>
|
||||
BlocBuilder<Cubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
final cubit = context.read<Cubit>();
|
||||
final cubit = context.watch<Cubit>();
|
||||
final inputValid = state.form.validatorOf(field).valid;
|
||||
return builder.call(context, cubit, state, field, inputValid);
|
||||
},
|
||||
|
@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart';
|
||||
import 'package:wyatt_form_bloc/src/domain/repositories/form_repository.dart';
|
||||
import 'package:wyatt_form_bloc/src/presentation/features/form_data/form_data_cubit.dart';
|
||||
|
||||
class InputBuilderTextController<Cubit extends FormDataCubit, S extends String?,
|
||||
Extra> extends StatelessWidget {
|
||||
InputBuilderTextController({
|
||||
required this.field,
|
||||
required this.builder,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String field;
|
||||
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
Cubit cubit,
|
||||
FormDataState state,
|
||||
String field,
|
||||
bool inputValid,
|
||||
TextEditingController textEditingController,
|
||||
FormInputMetadata<Extra>? metadata,
|
||||
) builder;
|
||||
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formName = context.watch<Cubit>().formName;
|
||||
final value =
|
||||
context.read<FormRepository>().accessForm(formName).valueOf<S>(field);
|
||||
_controller
|
||||
..text = value ?? ''
|
||||
..selection = TextSelection.fromPosition(
|
||||
TextPosition(
|
||||
offset: _controller.text.length,
|
||||
),
|
||||
);
|
||||
|
||||
return BlocBuilder<Cubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
final cubit = context.read<Cubit>();
|
||||
final inputValid = state.form.validatorOf(field).valid;
|
||||
final metadata = state.form.metadataOf<Extra>(field);
|
||||
return builder.call(
|
||||
context,
|
||||
cubit,
|
||||
state,
|
||||
field,
|
||||
inputValid,
|
||||
_controller,
|
||||
metadata,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -18,4 +18,5 @@ export 'features/form_data/form_data_cubit.dart';
|
||||
export 'features/form_data_impl/form_data_cubit_impl.dart';
|
||||
export 'features/widgets/input_builder.dart';
|
||||
export 'features/widgets/input_builder_metadata.dart';
|
||||
export 'features/widgets/input_builder_text_controller.dart';
|
||||
export 'features/widgets/submit_builder.dart';
|
||||
|
Loading…
x
Reference in New Issue
Block a user