Skip to content

Commit

Permalink
Merge pull request #50 from netglade/feat/42-ondeps-change
Browse files Browse the repository at this point in the history
Add onDepedencyChange
  • Loading branch information
petrnymsa authored Mar 26, 2024
2 parents faafed7 + 0fc289b commit a9e6ae1
Show file tree
Hide file tree
Showing 28 changed files with 500 additions and 247 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"dart.flutterSdkPath": ".fvm/versions/3.16.5",
"dart.lineLength": 120
"dart.lineLength": 120,
"yaml.schemaStore.enable": false
}
5 changes: 5 additions & 0 deletions glade_forms/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
## 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 longer passed into `onChange` and in validator.
- **[Breaking]**: GladeInput is no longer ChangeNotifier
- **[Add]**: onDependencyChange - callback is called when any (or multiple with groupEdit()) dependency was udpated.
- **Improvement**: GladeModelDebugInfo now colorize String values to visualize whitespace.

## 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: (keys) {
if (keys.contains('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 updating the `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)

The second approach is to use `dependencies` and `onDependencyChange` callback and react when different dependencies are changed. Note that it works with groupEdit() as well. In that case, onDependencyChange is called once for every changed dependency.

In this example, when age-input update 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.contains('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 a9e6ae1

Please sign in to comment.