diff --git a/src/Squidex/app/framework/angular/forms/error-formatting.spec.ts b/src/Squidex/app/framework/angular/forms/error-formatting.spec.ts index f6ac790c3..88b4206ad 100644 --- a/src/Squidex/app/framework/angular/forms/error-formatting.spec.ts +++ b/src/Squidex/app/framework/angular/forms/error-formatting.spec.ts @@ -132,6 +132,12 @@ describe('formatErrors', () => { expect(error).toEqual('MY_FIELD contains an invalid value: 4.'); }); + it('should format uniqueStrings', () => { + const error = validate(['1', '2', '2', '3'], ValidatorsEx.uniqueStrings()); + + expect(error).toEqual('MY_FIELD must not contain duplicate values.'); + }); + it('should format match', () => { const formControl1 = new FormControl(1); const formControl2 = new FormControl(2); diff --git a/src/Squidex/app/framework/angular/forms/error-formatting.ts b/src/Squidex/app/framework/angular/forms/error-formatting.ts index a54d23941..1b479eb8e 100644 --- a/src/Squidex/app/framework/angular/forms/error-formatting.ts +++ b/src/Squidex/app/framework/angular/forms/error-formatting.ts @@ -26,6 +26,7 @@ const DEFAULT_ERRORS: { [key: string]: string } = { patternmessage: '{message}', required: '{field} is required.', requiredTrue: '{field} is required.', + uniquestrings: '{field} must not contain duplicate values.', validdatetime: '{field} is not a valid date time.', validvalues: '{field} is not a valid value.', validarrayvalues: '{field} contains an invalid value: {invalidvalue}.' diff --git a/src/Squidex/app/framework/angular/forms/validators.spec.ts b/src/Squidex/app/framework/angular/forms/validators.spec.ts index 1e0911720..9cd351171 100644 --- a/src/Squidex/app/framework/angular/forms/validators.spec.ts +++ b/src/Squidex/app/framework/angular/forms/validators.spec.ts @@ -371,4 +371,38 @@ describe('ValidatorsEx.pattern', () => { expect(error).toEqual(expected); }); +}); + +describe('ValidatorsEx.uniqueStrings', () => { + it('should return null when value is null', () => { + const input = new FormControl(null); + + const error = ValidatorsEx.uniqueStrings()(input); + + expect(error).toBeNull(); + }); + + it('should return null when value is not a string array', () => { + const input = new FormControl([1, 2, 3]); + + const error = ValidatorsEx.uniqueStrings()(input); + + expect(error).toBeNull(); + }); + + it('should return null when values are unique', () => { + const input = new FormControl(['1', '2', '3']); + + const error = ValidatorsEx.uniqueStrings()(input); + + expect(error).toBeNull(); + }); + + it('should return error when values are not unique', () => { + const input = new FormControl(['1', '2', '2', '3']); + + const error = ValidatorsEx.uniqueStrings()(input); + + expect(error).toEqual({ uniquestrings: false }); + }); }); \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/validators.ts b/src/Squidex/app/framework/angular/forms/validators.ts index a0c61f551..3a08ed7c4 100644 --- a/src/Squidex/app/framework/angular/forms/validators.ts +++ b/src/Squidex/app/framework/angular/forms/validators.ts @@ -7,7 +7,7 @@ import { AbstractControl, ValidatorFn, Validators } from '@angular/forms'; -import { DateTime } from '@app/framework/internal'; +import { DateTime, Types } from '@app/framework/internal'; function isEmptyInputValue(value: any): boolean { return value == null || value.length === 0; @@ -175,4 +175,25 @@ export module ValidatorsEx { return null; }; } + + export function uniqueStrings(): ValidatorFn { + return (control: AbstractControl) => { + if (isEmptyInputValue(control.value) || !Types.isArrayOfString(control.value)) { + return null; + } + + const a: string[] = control.value; + const unique: { [key: string]: boolean } = {}; + + for (let value of a) { + if (unique[value]) { + return { uniquestrings: false }; + } else { + unique[value] = true; + } + } + + return null; + }; + } } \ No newline at end of file