Skip to content

Commit 5aa7db6

Browse files
committed
#488 - Enhance FormControl reset method
- This introduces a `nonNullable` property to the `FormControl` constructor to control the behavior of the `reset()` method. - When `nonNullable` is `true` (the default), calling `reset()` without a value will reset the control to its initial value. - When `nonNullable` is `false`, calling `reset()` without a value will reset the control to `null`. - Documentation for the constructor and the `reset` method has been updated to reflect this new behavior, including new examples. - Added a new unit test to verify the `nonNullable: false` behavior.
1 parent 33e2906 commit 5aa7db6

File tree

3 files changed

+108
-27
lines changed

3 files changed

+108
-27
lines changed

lib/src/models/models.dart

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,13 @@ class FormControl<T> extends AbstractControl<T> {
848848
///
849849
/// The control can optionally be initialized with a [value].
850850
///
851+
/// The [nonNullable] argument is used to determine the state of the control
852+
/// when the [reset] method is called without a value. If [nonNullable] is
853+
/// true (the default), the [reset] method will reset the control to the
854+
/// initial [value] provided in the constructor. If [nonNullable] is false,
855+
/// the [reset] method will reset the control to `null` unless a value is
856+
/// provided.
857+
///
851858
/// The control can optionally have [validators] that validates
852859
/// the control each time the value changes.
853860
///
@@ -859,10 +866,6 @@ class FormControl<T> extends AbstractControl<T> {
859866
/// (such as an HTTP request) if the more basic validation methods have
860867
/// already found invalid input.
861868
///
862-
/// You can set an [asyncValidatorsDebounceTime] in millisecond to set
863-
/// a delay time before trigger async validators. This is useful for
864-
/// minimizing request to a server. The default value is 250 milliseconds.
865-
///
866869
/// You can set [touched] as true to force the validation messages
867870
/// to show up at the very first time the widget that is bound to this
868871
/// control builds in the UI.
@@ -875,11 +878,8 @@ class FormControl<T> extends AbstractControl<T> {
875878
/// ```
876879
///
877880
FormControl({
878-
@Deprecated(
879-
"Use [defaultValue] to specify the initial default value for the form control.",
880-
)
881881
T? value,
882-
T? defaultValue,
882+
bool nonNullable = true,
883883
super.validators,
884884
super.asyncValidators,
885885
@Deprecated(
@@ -891,7 +891,7 @@ class FormControl<T> extends AbstractControl<T> {
891891
super.asyncValidatorsDebounceTime,
892892
super.touched,
893893
super.disabled,
894-
}) : _defaultValue = defaultValue ?? value {
894+
}) : _defaultValue = nonNullable ? value : null {
895895
if (value != null) {
896896
this.value = value;
897897
} else {
@@ -900,6 +900,13 @@ class FormControl<T> extends AbstractControl<T> {
900900
}
901901

902902
/// Gets the default value of the control.
903+
///
904+
/// This value is determined by the [value] and [nonNullable] arguments
905+
/// passed to the constructor:
906+
/// - If [nonNullable] is `true` (the default), this holds the initial [value].
907+
/// - If [nonNullable] is `false`, this is `null`.
908+
///
909+
/// When [reset] is called without a value, the control resets to this value.
903910
T? get defaultValue => _defaultValue;
904911

905912
/// True if the control is marked as focused.
@@ -1023,6 +1030,74 @@ class FormControl<T> extends AbstractControl<T> {
10231030
}
10241031
}
10251032

1033+
/// Resets the form control, marking it as untouched and pristine.
1034+
///
1035+
/// If [value] is provided, the control is reset to that value.
1036+
///
1037+
/// If [value] is not provided (null), the behavior depends on the [nonNullable]
1038+
/// argument passed to the constructor:
1039+
/// - If [nonNullable] is `true` (the default), the control resets to the
1040+
/// initial value provided in the constructor.
1041+
/// - If [nonNullable] is `false`, the control resets to `null`.
1042+
///
1043+
/// The argument [disabled] is optional and resets the disabled status of the
1044+
/// control. If value is `true` then it will disable the control, if value is
1045+
/// `false` then it will enable the control, and if the value is `null` or
1046+
/// not set (the default) then the control will state in the same state that
1047+
/// it previously was.
1048+
///
1049+
/// The argument [removeFocus] is optional and remove the UI focus from the
1050+
/// control.
1051+
///
1052+
/// When [updateParent] is true or not supplied (the default) each change
1053+
/// affects this control and its parent, otherwise only affects to this
1054+
/// control.
1055+
///
1056+
/// When [emitEvent] is true or not supplied (the default), both the
1057+
/// *statusChanges* and *valueChanges* events notify listeners with the
1058+
/// latest status and value when the control is reset. When false, no events
1059+
/// are emitted.
1060+
///
1061+
/// ### Examples
1062+
///
1063+
/// **Reset to a specific value**
1064+
/// ```dart
1065+
/// final control = FormControl<String>();
1066+
///
1067+
/// control.reset(value: 'John Doe');
1068+
///
1069+
/// print(control.value); // output: 'John Doe'
1070+
/// ```
1071+
///
1072+
/// **Reset to initial value (nonNullable: true)**
1073+
/// ```dart
1074+
/// // nonNullable is true by default
1075+
/// final control = FormControl<String>(value: 'Initial Value');
1076+
///
1077+
/// control.value = 'New Value';
1078+
///
1079+
/// // Resets to 'Initial Value' because no value was provided
1080+
/// // and nonNullable is true.
1081+
/// control.reset();
1082+
///
1083+
/// print(control.value); // output: 'Initial Value'
1084+
/// ```
1085+
///
1086+
/// **Reset to null (nonNullable: false)**
1087+
/// ```dart
1088+
/// final control = FormControl<String>(
1089+
/// value: 'Initial Value',
1090+
/// nonNullable: false,
1091+
/// );
1092+
///
1093+
/// control.value = 'New Value';
1094+
///
1095+
/// // Resets to null because no value was provided
1096+
/// // and nonNullable is false.
1097+
/// control.reset();
1098+
///
1099+
/// print(control.value); // output: null
1100+
///
10261101
@override
10271102
void reset({
10281103
T? value,

test/src/models/form_control_test.dart

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ void main() {
415415
'resets to initial value (null) if no argument is provided and initial value was null',
416416
() {
417417
// Arrange
418-
final control = FormControl<String?>(value: null);
418+
final control = FormControl<String>(value: null);
419419
control.updateValue('changed');
420420

421421
// Act
@@ -430,7 +430,7 @@ void main() {
430430
'resets to initial value (null) if no argument is provided and no initial value was specified (implicitly null)',
431431
() {
432432
// Arrange
433-
final control = FormControl<String?>();
433+
final control = FormControl<String>();
434434
control.updateValue('changed');
435435

436436
// Act
@@ -460,7 +460,7 @@ void main() {
460460
'resets to initial value when value argument is null and initial value was not null',
461461
() {
462462
// Arrange
463-
final control = FormControl<String?>(value: 'initial');
463+
final control = FormControl<String>(value: 'initial');
464464
control.updateValue('changed');
465465

466466
// Act
@@ -470,25 +470,37 @@ void main() {
470470
expect(
471471
control.value,
472472
'initial',
473-
); // Because (value ?? _initialValue) => (null ?? "initial") => "initial"
473+
); // Because nonNullable is true by default
474474
},
475475
);
476476

477+
test('resets to null is nonNullable is false', () {
478+
// Arrange
479+
final control = FormControl<String>(
480+
value: 'initialValue',
481+
nonNullable: false,
482+
);
483+
control.value = 'changed value';
484+
485+
// Act
486+
control.reset();
487+
488+
// Assert
489+
expect(control.value, null); // Because nonNullable is false
490+
});
491+
477492
test(
478-
'resets to null when value argument is null and initial value was also null',
493+
'resets control to null if no value provided and nonNullable is false',
479494
() {
480495
// Arrange
481-
final control = FormControl<String?>(value: null);
482-
control.updateValue('changed');
496+
final control = FormControl<String>(nonNullable: false);
497+
control.value = 'changed value';
483498

484499
// Act
485-
control.reset(value: null);
500+
control.reset();
486501

487502
// Assert
488-
expect(
489-
control.value,
490-
null,
491-
); // Because (value ?? _initialValue) => (null ?? null) => null
503+
expect(control.value, null); // Because no value provided
492504
},
493505
);
494506
});

test/src/models/form_group_test.dart

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,14 +288,8 @@ void main() {
288288
});
289289

290290
// When: enable form
291-
print("form.enabled: ${form.enabled}");
292-
293291
form.markAsEnabled();
294292

295-
form.controls.forEach((name, control) {
296-
print("control[$name].enabled: ${control.enabled}");
297-
});
298-
299293
// Then: all controls are enabled
300294
expect(
301295
form.controls.values.every((control) => control.enabled),

0 commit comments

Comments
 (0)