]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: CdFormGroup
authorStephan Müller <smueller@suse.com>
Mon, 18 Jun 2018 10:41:36 +0000 (12:41 +0200)
committerStephan Müller <smueller@suse.com>
Tue, 3 Jul 2018 15:42:32 +0000 (17:42 +0200)
CdFormGroup extends 'FormGroup' with a few new methods that will help
form development.

Signed-off-by: Stephan Müller <smueller@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-form-group.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-form-group.ts [new file with mode: 0644]

diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-form-group.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-form-group.spec.ts
new file mode 100644 (file)
index 0000000..ab3cd74
--- /dev/null
@@ -0,0 +1,191 @@
+import { AbstractControl, FormControl, FormGroup, NgForm } from '@angular/forms';
+
+import { CdFormGroup } from './cd-form-group';
+
+describe('FormsHelperService', () => {
+  let form: CdFormGroup;
+
+  const createTestForm = (controlName: string, value: any): FormGroup =>
+    new FormGroup({
+      [controlName]: new FormControl(value)
+    });
+
+  describe('test get and getValue in nested forms', () => {
+    let formA: FormGroup;
+    let formB: FormGroup;
+    let formC: FormGroup;
+
+    beforeEach(() => {
+      formA = createTestForm('a', 'a');
+      formB = createTestForm('b', 'b');
+      formC = createTestForm('c', 'c');
+      form = new CdFormGroup({
+        formA: formA,
+        formB: formB,
+        formC: formC
+      });
+    });
+
+    it('should find controls out of every form', () => {
+      expect(form.get('a')).toBe(formA.get('a'));
+      expect(form.get('b')).toBe(formB.get('b'));
+      expect(form.get('c')).toBe(formC.get('c'));
+    });
+
+    it('should throw an error if element could be found', () => {
+      expect(() => form.get('d')).toThrowError('Control \'d\' could not be found!');
+      expect(() => form.get('sth')).toThrowError('Control \'sth\' could not be found!');
+    });
+  });
+
+  describe('CdFormGroup tests', () => {
+    let x, nested, a, c;
+
+    beforeEach(() => {
+      a = new FormControl('a');
+      x = new CdFormGroup({
+        a: a
+      });
+      nested = new CdFormGroup({
+        lev1: new CdFormGroup({
+          lev2: new FormControl('lev2')
+        })
+      });
+      c = createTestForm('c', 'c');
+      form = new CdFormGroup({
+        nested: nested,
+        cdform: x,
+        b: new FormControl('b'),
+        formC: c
+      });
+    });
+
+    it('should return single value from "a" control in not nested form "x"', () => {
+      expect(x.get('a')).toBe(a);
+      expect(x.getValue('a')).toBe('a');
+    });
+
+    it('should return control "a" out of form "x" in nested form', () => {
+      expect(form.get('a')).toBe(a);
+      expect(form.getValue('a')).toBe('a');
+    });
+
+    it('should return value "b" that is not nested in nested form', () => {
+      expect(form.getValue('b')).toBe('b');
+    });
+
+    it('return value "c" out of normal form group "c" in nested form', () => {
+      expect(form.getValue('c')).toBe('c');
+    });
+
+    it('should return "lev2" value', () => {
+      expect(form.getValue('lev2')).toBe('lev2');
+    });
+
+    it('should nested throw an error if control could not be found', () => {
+      expect(() => form.get('d')).toThrowError('Control \'d\' could not be found!');
+      expect(() => form.getValue('sth')).toThrowError('Control \'sth\' could not be found!');
+    });
+  });
+
+  describe('test different values for getValue', () => {
+    beforeEach(() => {
+      form = new CdFormGroup({
+        form_undefined: createTestForm('undefined', undefined),
+        form_null: createTestForm('null', null),
+        form_emptyObject: createTestForm('emptyObject', {}),
+        form_filledObject: createTestForm('filledObject', { notEmpty: 1 }),
+        form_number0: createTestForm('number0', 0),
+        form_number1: createTestForm('number1', 1),
+        form_emptyString: createTestForm('emptyString', ''),
+        form_someString1: createTestForm('someString1', 's'),
+        form_someString2: createTestForm('someString2', 'sth'),
+        form_floating: createTestForm('floating', 0.1),
+        form_false: createTestForm('false', false),
+        form_true: createTestForm('true', true)
+      });
+    });
+
+    it('returns objects', () => {
+      expect(form.getValue('null')).toBe(null);
+      expect(form.getValue('emptyObject')).toEqual({});
+      expect(form.getValue('filledObject')).toEqual({ notEmpty: 1 });
+    });
+
+    it('returns set numbers', () => {
+      expect(form.getValue('number0')).toBe(0);
+      expect(form.getValue('number1')).toBe(1);
+      expect(form.getValue('floating')).toBe(0.1);
+    });
+
+    it('returns strings that are not empty', () => {
+      expect(form.getValue('someString1')).toBe('s');
+      expect(form.getValue('someString2')).toBe('sth');
+    });
+
+    it('returns booleans', () => {
+      expect(form.getValue('true')).toBe(true);
+      expect(form.getValue('false')).toBe(false);
+    });
+
+    it('returns null if control was set as undefined', () => {
+      expect(form.getValue('undefined')).toBe(null);
+    });
+
+    it('returns a falsy value for empty string, null, undefined, false and 0', () => {
+      expect(form.getValue('emptyString')).toBe(false);
+      expect(form.getValue('false')).toBeFalsy();
+      expect(form.getValue('null')).toBeFalsy();
+      expect(form.getValue('number0')).toBeFalsy();
+    });
+
+    it('test _filterValue', () => {
+      expect(form._filterValue(0)).toBe(true);
+      expect(form._filterValue(null)).toBe(true);
+      expect(form._filterValue(false)).toBe(true);
+      expect(form._filterValue('')).toBe(false);
+    });
+  });
+
+  describe('should test showError', () => {
+    let formDir: NgForm;
+    let test: AbstractControl;
+
+    beforeEach(() => {
+      formDir = new NgForm([], []);
+      form = new CdFormGroup({
+        test_form: createTestForm('test', '')
+      });
+      test = form.get('test');
+      test.setErrors({ someError: 'failed' });
+    });
+
+    it('should not show an error if not dirty and not submitted', () => {
+      expect(form.showError('test', formDir)).toBe(false);
+    });
+
+    it('should show an error if dirty', () => {
+      test.markAsDirty();
+      expect(form.showError('test', formDir)).toBe(true);
+    });
+
+    it('should show an error if submitted', () => {
+      expect(form.showError('test', <NgForm>{ submitted: true })).toBe(true);
+    });
+
+    it('should not show an error if no error exits', () => {
+      test.setErrors(null);
+      expect(form.showError('test', <NgForm>{ submitted: true })).toBe(false);
+      test.markAsDirty();
+      expect(form.showError('test', formDir)).toBe(false);
+    });
+
+    it('should not show error if the given error is not there', () => {
+      expect(form.showError('test', <NgForm>{ submitted: true }, 'someOtherError')).toBe(false);
+    });
+
+    it('should show error if the given error is there', () => {
+      expect(form.showError('test', <NgForm>{ submitted: true }, 'someError')).toBe(true);
+    });
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-form-group.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-form-group.ts
new file mode 100644 (file)
index 0000000..8dfe8c5
--- /dev/null
@@ -0,0 +1,95 @@
+import {
+  AbstractControl,
+  AbstractControlOptions,
+  AsyncValidatorFn,
+  FormGroup,
+  NgForm,
+  ValidatorFn
+} from '@angular/forms';
+
+/**
+ * CdFormGroup extends FormGroup with a few new methods that will help form development.
+ */
+export class CdFormGroup extends FormGroup {
+  constructor(
+    public controls: { [key: string]: AbstractControl },
+    validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null,
+    asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null
+  ) {
+    super(controls, validatorOrOpts, asyncValidator);
+  }
+
+  /**
+   * Get a control out of any control even if its nested in other CdFormGroups or a FormGroup
+   *
+   * @param {string} controlName
+   * @returns {AbstractControl}
+   */
+  get(controlName: string): AbstractControl {
+    const control = this._get(controlName);
+    if (!control) {
+      throw new Error(`Control '${controlName}' could not be found!`);
+    }
+    return control;
+  }
+
+  _get(controlName): AbstractControl {
+    return (
+      super.get(controlName) ||
+      Object.values(this.controls)
+        .filter((c) => c.get)
+        .map((form) => {
+          if (form instanceof CdFormGroup) {
+            return form._get(controlName);
+          }
+          return form.get(controlName);
+        })
+        .find((c) => Boolean(c))
+    );
+  }
+
+  /**
+   * Get the value of a control if it has none it will return false
+   *
+   * @param {string} controlName
+   * @returns {any} false or the value of the control
+   */
+  getValue(controlName: string): any {
+    const value = this.get(controlName).value;
+    return this._filterValue(value) && value;
+  }
+
+  // Overwrite this if needed.
+  _filterValue(value) {
+    return value !== '';
+  }
+
+  /**
+   * Sets a control without triggering a value changes event
+   *
+   * Very useful if a function is called through a value changes event but the value
+   * should be changed within the call.
+   *
+   * @param {string} controlName
+   * @param value
+   */
+  silentSet(controlName: string, value: any) {
+    this.get(controlName).setValue(value, { emitEvent: false });
+  }
+
+  /**
+   * Indicates errors of the control in templates
+   *
+   * @param {string} controlName
+   * @param {NgForm} form
+   * @param {string} errorName
+   * @returns {boolean}
+   */
+  showError(controlName: string, form: NgForm, errorName?: string) {
+    const control = this.get(controlName);
+    return (
+      (form.submitted || control.dirty) &&
+      (errorName ? control.hasError(errorName) : control.invalid)
+    );
+  }
+}