Skip to content

Commit

Permalink
Add onDependencyChange, remove extra parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
petrnymsa committed Mar 25, 2024
1 parent faafed7 commit 71deb32
Show file tree
Hide file tree
Showing 22 changed files with 322 additions and 218 deletions.
2 changes: 2 additions & 0 deletions glade_forms/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## 2.0.0
- **[Breaking]**: TextEditingController is no more created automatically. When TextEditingController is used, input's behavior is slightly changed. See README.md for full info.
- **[Breaking]**: GladeInput's controller is now private. Use factory constructors to create input.
- **[Breaking]**: `Extra` parameter removed
- **[Breaking]**: `dependencies` are no loonger passed into `onChange` and in validator.

## 1.6.0
- **Improvement**: GladeModelDebugInfo is more colorful and polished.
Expand Down
96 changes: 42 additions & 54 deletions glade_forms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ A universal way to define form validators with support of translations.
- [Inputs](#inputs)
- [Flutter widgets](#flutter-widgets)
- [Edit multiple inputs at once](#edit-multiple-inputs-at-once)
- [Dependencies (WIP)](#dependencies-wip)
- [Dependencies](#dependencies)
- [Controlling other inputs](#controlling-other-inputs)
- [Translation](#translation)
- [Default translations](#default-translations)
Expand Down Expand Up @@ -179,9 +179,7 @@ ageInput = GladeInput.create(
validator: (v) => (v
..notNull()
..satisfy(
(value, extra, dependencies) {
return value >= 18;
},
(value) => value >= 18,
devError: (_, __) => 'Value must be greater or equal to 18',
key: _ErrorKeys.ageRestriction,
))
Expand Down Expand Up @@ -267,66 +265,35 @@ class FormModel extends GladeModel {

After that listener will contain `lastUpdatedKeys` with keys of `age` and `name` inputs.

### Dependencies (WIP)
**NOTICE** - in future versions, dependencies will be used only for dependent listenner. Use your variables directly without need to lookup dependencies.
### Dependencies
An input can depend on other inputs to enable updates based on those dependencies. To define these dependencies, use the dependencies attribute. It's essential to specify inputKey on any inputs that are intended to serve as dependencies.

Input can have dependencies on other inputs to allow dependent validation. Define input's dependencies with `dependencies`.

`inputKey` must be specified on inputs to be used as dependencies.

In validation, translation or in `onChange()`, just call `dependencies.byKey()` to get dependent input.

Note that `byKey()` will throw if no input is found. This is by design to provide immediate indication of error.

For example, we want to restrict "Age input" to be at least 18 when "VIP Content" is checked.
For instance, consider a scenario where we want the "VIP Content" option to be automatically selected when the 'ageInput' is updated and its value exceeds 18.

```dart
ageInput = GladeInput.create(
validator: (v) => (v
..notNull()
..satisfy(
(value, extra, dependencies) {
final vipContentInput = dependencies.byKey<bool>('vip-input');
if (!vipContentInput.value) {
return true;
}
return value >= 18;
},
devError: (_, __) => 'When VIP enabled you must be at least 18 years old.',
key: _ErrorKeys.ageRestriction,
))
.build(),
value: 0,
dependencies: () => [vipInput], // <--- Dependency
valueConverter: GladeTypeConverters.intConverter,
inputKey: 'age-input',
translateError: (error, key, devMessage, dependencies) {
if (key == _ErrorKeys.ageRestriction) return LocaleKeys.ageRestriction_under18.tr();
if (error.isConversionError) return LocaleKeys.ageRestriction_ageFormat.tr();
return devMessage;
},
);
vipInput = GladeInput.create(
validator: (v) => (v..notNull()).build(),
value: false,
inputKey: 'vip-input',
);
ageInput = GladeInput.create(
value: 0,
valueConverter: GladeTypeConverters.intConverter,
inputKey: 'age-input',
);
vipInput = GladeInput.create(
inputKey: 'vip-input',
dependencies: () => [ageInput],
onDependencyChange: (key) {
if (key == 'age-input') {
vipInput.value = ageInput.value >= 18;
}
},
);
```

![dependent-validation](https://raw.githubusercontent.com/netglade/glade_forms/main/glade_forms/doc/depend-validation.gif)

### Controlling other inputs

Sometimes, it can be handy to update some input's value based on the changed value of another input.
Sometimes, it can be handy to update some input's value based on the changed value of another input. As developer you have two options.

Each input has `onChange()` callback where these reactions can be created.

An example could be automatically update `Age` value based on checked `VIP Content` input (checkbox).
You can listen for `onChange()` callback and update other inputs based on input's changed value. An example could be automatically update `Age` value based on checked `VIP Content` input (checkbox).

```dart
// In vipContent input
Expand All @@ -339,6 +306,27 @@ onChange: (info, dependencies) {

![two-way-inputs-example](https://raw.githubusercontent.com/netglade/glade_forms/main/glade_forms/doc/two-way-dependencies.gif)

Second approach is to use `dependencies` and `onDependencyChange` callback and react when different input was changed.

In this example, when age-input updats its value (dependency), checkbox's value (vipInput) is updated.

```dart
vipInput = GladeInput.create(
inputKey: 'vip-input',
dependencies: () => [ageInput],
onChange: (info) {
if (info.value && ageInput.value < 18) {
ageInput.value = 18;
}
},
onDependencyChange: (key) {
if (key == 'age-input') {
vipInput.value = ageInput.value >= 18;
}
},
);
```

### Translation

Each validation error (and conversion error if any) can be translated. Provide `translateError` function which accepts:
Expand Down
Binary file modified glade_forms/doc/depend-validation.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion glade_forms/lib/src/core/changes_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart';
import 'package:glade_forms/src/validator/validator_result.dart';

class ChangesInfo<T> extends Equatable {
final String inputKey;
final T? initialValue;
final T? previousValue;

Expand All @@ -10,8 +11,10 @@ class ChangesInfo<T> extends Equatable {
final ValidatorResult<T>? validatorResult;

@override
List<Object?> get props => [initialValue, previousValue, value, validatorResult];
List<Object?> get props => [initialValue, previousValue, value, validatorResult, inputKey];

const ChangesInfo({
required this.inputKey,
required this.previousValue,
required this.value,
required this.validatorResult,
Expand Down
6 changes: 3 additions & 3 deletions glade_forms/lib/src/core/convert_error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:equatable/equatable.dart';
import 'package:glade_forms/src/core/glade_input_error.dart';

/// Before validation when converer from string to prpoer type failed.
typedef OnConvertError = String Function(String? rawInput, {Object? extra, Object? key});
typedef OnConvertError = String Function(String? rawInput, {Object? key});

class ConvertError<T> extends GladeInputError<T> with EquatableMixin implements Exception {
// ignore: prefer-correct-callback-field-name, more suitable name
Expand All @@ -20,7 +20,7 @@ class ConvertError<T> extends GladeInputError<T> with EquatableMixin implements

String get targetType => T.runtimeType.toString();

String get devErrorMessage => devError(input, extra: error, key: key);
String get devErrorMessage => devError(input, key: key);

@override
// ignore: no-object-declaration, error can be any object.
Expand All @@ -32,7 +32,7 @@ class ConvertError<T> extends GladeInputError<T> with EquatableMixin implements
super.key,
OnConvertError? formatError,
}) : _convertError = error,
devError = formatError ?? ((rawValue, {extra, key}) => 'Conversion error: $error');
devError = formatError ?? ((rawValue, {key}) => 'Conversion error: $error');

@override
String toString() => devError(input, key: key);
Expand Down
Loading

0 comments on commit 71deb32

Please sign in to comment.