Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RequiredNonEmptyValidator #380

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ There are common predefined validators, but you can implement custom validators
#### FormControl

- Validators.required
- Validators.requiredNonEmpty
- Validators.requiredTrue
- Validators.email
- Validators.number
Expand Down
29 changes: 29 additions & 0 deletions lib/src/validators/required_non_empty_validator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2020 Joan Pablo Jimenez Milian. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

import 'package:reactive_forms/reactive_forms.dart';

/// Validator that requires the control's value to not be null or empty.
class RequiredNonEmptyValidator extends Validator<dynamic> {
const RequiredNonEmptyValidator() : super();
@override
Map<String, dynamic>? validate(AbstractControl<dynamic> control) {
final error = <String, dynamic>{ValidationMessage.requiredNonEmpty: true};
final dynamic value = control.value;

if (value == null) {
return error;
} else if (value is String) {
return value.trim().isEmpty ? error : null;
} else if (value is Iterable && value.isEmpty) {
return error;
} else if (value is Map && value.isEmpty) {
return error;
} else if (value is Set && value.isEmpty) {
return error;
}

return null;
}
}
2 changes: 1 addition & 1 deletion lib/src/validators/required_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import 'package:reactive_forms/reactive_forms.dart';

/// Validator that requires the control have a non-empty value.
/// Validator that requires the control's value to not be null or blank string.
class RequiredValidator extends Validator<dynamic> {
const RequiredValidator() : super();

Expand Down
3 changes: 3 additions & 0 deletions lib/src/validators/validation_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class ValidationMessage {
/// Key text for required validation message.
static const String required = 'required';

/// Key text for `requiredNonEmpty` validation message.
static const String requiredNonEmpty = 'requiredNonEmpty';

/// Key text for pattern validation message.
static const String pattern = 'pattern';

Expand Down
10 changes: 9 additions & 1 deletion lib/src/validators/validators.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import 'package:reactive_forms/src/validators/pattern/default_pattern_evaluator.
import 'package:reactive_forms/src/validators/pattern/pattern_evaluator.dart';
import 'package:reactive_forms/src/validators/pattern/regex_pattern_evaluator.dart';
import 'package:reactive_forms/src/validators/pattern_validator.dart';
import 'package:reactive_forms/src/validators/required_non_empty_validator.dart';
import 'package:reactive_forms/src/validators/required_validator.dart';

/// Provides a set of built-in validators that can be used by form controls,
Expand All @@ -39,9 +40,16 @@ class Validators {
AsyncValidatorFunction validator) =>
DelegateAsyncValidator(validator);

/// Gets a validator that requires the control have a non-empty value.
/// Gets a validator that requires the control's value to not be null or
/// blank string.
static Validator<dynamic> get required => const RequiredValidator();

/// Gets a validator that requires the control's value be non-empty.
/// This validator is commonly used for fields that are both required and
/// should have a non-empty value.
static Validator<dynamic> get requiredNonEmpty =>
const RequiredNonEmptyValidator();

/// Gets a validator that requires the control's value be true.
/// This validator is commonly used for required checkboxes.
static Validator<dynamic> get requiredTrue => const EqualsValidator<bool>(
Expand Down
110 changes: 110 additions & 0 deletions test/src/validators/required_non_empty_validator_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:reactive_forms/reactive_forms.dart';

void main() {
group('Required Non Empty validator tests', () {
test('FormControl is invalid if value is null', () {
// Given: an invalid control
final control = FormControl<dynamic>(
value: null,
validators: [Validators.requiredNonEmpty],
);

// Expect: control is invalid
expect(control.valid, false);
expect(control.errors, {ValidationMessage.requiredNonEmpty: true});
});

test('FormControl is valid if value is not null', () {
// Given: a valid control
final control = FormControl<num>(
value: 42,
validators: [Validators.requiredNonEmpty],
);

// Expect: control is valid
expect(control.valid, true);
});

test('FormControl is invalid if string value is null', () {
// Given: an invalid control
final control = FormControl<String>(
value: null,
validators: [Validators.requiredNonEmpty],
);

// Expect: control is invalid
expect(control.valid, false);
expect(control.errors, {ValidationMessage.requiredNonEmpty: true});
});

test('FormControl is invalid if string value is empty', () {
// Given: an invalid control
final control = FormControl<String>(
value: " ",
validators: [Validators.requiredNonEmpty],
);

// Expect: control is invalid
expect(control.valid, false);
expect(control.errors, {ValidationMessage.requiredNonEmpty: true});
});

test('FormControl is valid if string value is not null', () {
// Given: a valid control
final control = FormControl<String>(
value: "42",
validators: [Validators.requiredNonEmpty],
);

// Expect: control is valid
expect(control.valid, true);
});

test('FormControl is invalid if value is an emtpy list', () {
// Given: a valid control
final control = FormControl<List<dynamic>>(
value: <dynamic>[],
validators: [Validators.requiredNonEmpty],
);

// Expect: control is invalid
expect(control.valid, false);
expect(control.errors, {ValidationMessage.requiredNonEmpty: true});
});

test('FormControl is valid if value is non emtpy list', () {
// Given: a valid control
final control = FormControl<List<dynamic>>(
value: <dynamic>[1],
validators: [Validators.requiredNonEmpty],
);

// Expect: control is valid
expect(control.valid, true);
});

test('FormControl is invalid if value is an emtpy map', () {
// Given: a valid control
final control = FormControl<Map<String, dynamic>>(
value: <String, dynamic>{},
validators: [Validators.requiredNonEmpty],
);

// Expect: control is invalid
expect(control.valid, false);
expect(control.errors, {ValidationMessage.requiredNonEmpty: true});
});

test('FormControl is valid if value is non emtpy map', () {
// Given: a valid control
final control = FormControl<Map<String, dynamic>>(
value: <String, dynamic>{"answer": 42},
validators: [Validators.requiredNonEmpty],
);

// Expect: control is valid
expect(control.valid, true);
});
});
}