--- /dev/null
+import { FormControl, FormGroup } from '@angular/forms';
+
+import { CdValidators } from './cd-validators';
+
+describe('CdValidators', () => {
+ describe('email', () => {
+ it('should not error on an empty email address', () => {
+ const control = new FormControl('');
+ expect(CdValidators.email(control)).toBeNull();
+ });
+
+ it('should not error on valid email address', () => {
+ const control = new FormControl('dashboard@ceph.com');
+ expect(CdValidators.email(control)).toBeNull();
+ });
+
+ it('should error on invalid email address', () => {
+ const control = new FormControl('xyz');
+ expect(CdValidators.email(control)).toEqual({'email': true});
+ });
+ });
+
+ describe('requiredIf', () => {
+ let form: FormGroup;
+
+ beforeEach(() => {
+ form = new FormGroup({
+ x: new FormControl(true),
+ y: new FormControl('abc'),
+ z: new FormControl('')
+ });
+ });
+
+ it('should not error because all conditions are fulfilled', () => {
+ form.get('z').setValue('zyx');
+ const validatorFn = CdValidators.requiredIf({
+ 'x': true,
+ 'y': 'abc'
+ });
+ expect(validatorFn(form.controls['z'])).toBeNull();
+ });
+
+ it('should not error because of unmet prerequisites', () => {
+ // Define prereqs that do not match the current values of the form fields.
+ const validatorFn = CdValidators.requiredIf({
+ 'x': false,
+ 'y': 'xyz'
+ });
+ // The validator must succeed because the prereqs do not match, so the
+ // validation of the 'z' control will be skipped.
+ expect(validatorFn(form.controls['z'])).toBeNull();
+ });
+
+ it('should error because of an empty value', () => {
+ // Define prereqs that force the validator to validate the value of
+ // the 'z' control.
+ const validatorFn = CdValidators.requiredIf({
+ 'x': true,
+ 'y': 'abc'
+ });
+ // The validator must fail because the value of control 'z' is empty.
+ expect(validatorFn(form.controls['z'])).toEqual({'required': true});
+ });
+
+ it('should not error because of unsuccessful condition', () => {
+ form.get('z').setValue('zyx');
+ // Define prereqs that force the validator to validate the value of
+ // the 'z' control.
+ const validatorFn = CdValidators.requiredIf({
+ 'x': true,
+ 'z': 'zyx'
+ }, () => false);
+ expect(validatorFn(form.controls['z'])).toBeNull();
+ });
+
+ it('should error because of successful condition', () => {
+ const conditionFn = (value) => {
+ return value === 'abc';
+ };
+ // Define prereqs that force the validator to validate the value of
+ // the 'y' control.
+ const validatorFn = CdValidators.requiredIf({
+ 'x': true,
+ 'z': ''
+ }, conditionFn);
+ expect(validatorFn(form.controls['y'])).toEqual({'required': true});
+ });
+ });
+});
--- /dev/null
+import {
+ AbstractControl,
+ ValidationErrors,
+ ValidatorFn,
+ Validators
+} from '@angular/forms';
+
+import * as _ from 'lodash';
+
+type Prerequisites = { // tslint:disable-line
+ [key: string]: any
+};
+
+export function isEmptyInputValue(value: any): boolean {
+ return value == null || value.length === 0;
+}
+
+export class CdValidators {
+ /**
+ * Validator that performs email validation. In contrast to the Angular
+ * email validator an empty email will not be handled as invalid.
+ */
+ static email(control: AbstractControl): ValidationErrors | null {
+ // Exit immediately if value is empty.
+ if (isEmptyInputValue(control.value)) {
+ return null;
+ }
+ return Validators.email(control);
+ }
+
+ /**
+ * Validator that requires controls to fulfill the specified condition if
+ * the specified prerequisites matches. If the prerequisites are fulfilled,
+ * then the given function is executed and if it succeeds, the 'required'
+ * validation error will be returned, otherwise null.
+ * @param {Prerequisites} prerequisites An object containing the prerequisites.
+ * ### Example
+ * ```typescript
+ * {
+ * 'generate_key': true,
+ * 'username': 'Max Mustermann'
+ * }
+ * ```
+ * Only if all prerequisites are fulfilled, then the validation of the
+ * control will be triggered.
+ * @param {Function | undefined} condition The function to be executed when all
+ * prerequisites are fulfilled. If not set, then the {@link isEmptyInputValue}
+ * function will be used by default. The control's value is used as function
+ * argument. The function must return true to set the validation error.
+ * @return {ValidatorFn} Returns the validator function.
+ */
+ static requiredIf(prerequisites: Prerequisites, condition?: Function | undefined): ValidatorFn {
+ return (control: AbstractControl): ValidationErrors | null => {
+ // Check if all prerequisites matches.
+ if (!Object.keys(prerequisites).every((key) => {
+ return (control.parent && control.parent.get(key).value === prerequisites[key]);
+ })) {
+ return null;
+ }
+ const success = _.isFunction(condition) ? condition.call(condition, control.value) :
+ isEmptyInputValue(control.value);
+ return success ? {'required': true} : null;
+ };
+ }
+}