This repository has been archived by the owner on May 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
618b4cb
commit b49b82f
Showing
11 changed files
with
270 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { | ||
AbstractControl, | ||
FormControl, | ||
ValidationErrors, | ||
ValidatorFn, | ||
} from '@angular/forms'; | ||
import { dependentValidator } from './validators'; | ||
|
||
function matchValidator<T>(compareVal: T): ValidatorFn { | ||
return function (control: AbstractControl): ValidationErrors | null { | ||
return control.value === compareVal | ||
? null | ||
: { match: { expected: compareVal, actual: control.value } }; | ||
}; | ||
} | ||
|
||
describe('dependentValidator', () => { | ||
let controlA: FormControl; | ||
let controlB: FormControl; | ||
|
||
let controlAValue: string; | ||
let controlBValue: string; | ||
|
||
// let matchValidatorSpy: jasmine.Spy<typeof matchValidator>; | ||
let condition: ((val?: any) => boolean) | undefined; | ||
|
||
Given(() => { | ||
controlAValue = ''; | ||
controlBValue = ''; | ||
condition = undefined; | ||
// matchValidatorSpy = jasmine.createSpy('matchValidator', matchValidator).and.callThrough(); | ||
}); | ||
|
||
When(() => { | ||
controlA = new FormControl(controlAValue); | ||
controlB = new FormControl( | ||
controlBValue, | ||
dependentValidator<string>({ | ||
watchControl: () => controlA, | ||
validator: (val) => matchValidator(val), | ||
condition, | ||
}) | ||
); | ||
}); | ||
|
||
describe('controlA.value === controlB.value', () => { | ||
Given(() => (controlAValue = '')); | ||
Then('Control B is valid', () => expect(controlB.valid).toBe(true)); | ||
}); | ||
|
||
describe('controlA.value !== controlB.value', () => { | ||
Given(() => (controlAValue = 'asd')); | ||
Then('Control B is invalid', () => expect(controlB.valid).toBe(false)); | ||
}); | ||
|
||
describe('controlA.value !== controlB.value, then updated to match', () => { | ||
Given(() => { | ||
controlAValue = 'asd'; | ||
controlBValue = 'qwe'; | ||
}); | ||
|
||
Then('Control B is valid', () => { | ||
controlA.setValue(controlBValue); | ||
expect(controlB.valid).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('condition is provided', () => { | ||
describe('GIVEN: condition returns false', () => { | ||
Given(() => { | ||
controlAValue = 'not Dima'; | ||
controlBValue = 'two'; | ||
condition = (val) => val === 'Dima'; | ||
}); | ||
|
||
Then('Control B is valid', () => { | ||
expect(controlB.valid).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('GIVEN: condition returns true', () => { | ||
Given(() => { | ||
controlAValue = 'Dima'; | ||
controlBValue = 'two'; | ||
condition = (val) => val === 'Dima'; | ||
}); | ||
|
||
Then('Control B is invalid', () => { | ||
expect(controlB.valid).toBe(false); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { isDevMode } from '@angular/core'; | ||
import { AbstractControl, ValidatorFn } from '@angular/forms'; | ||
|
||
export interface DependentValidatorOptions<T> { | ||
/** | ||
* Function that returns AbstractControl to watch | ||
* @param form - the root FormGroup of the control being validated | ||
*/ | ||
watchControl: (form?: AbstractControl) => AbstractControl; | ||
/** | ||
* @param watchControlValue - the value of the control being watched | ||
* @returns ValidatorFn. Ex: Validators.required | ||
*/ | ||
validator: (watchControlValue?: T) => ValidatorFn; | ||
/** | ||
* If the condition is provided, it must return true in order for the | ||
* validator to be applied. | ||
* @param watchControlValue - the value of the control being watched | ||
*/ | ||
condition?: (watchControlValue?: T) => boolean; | ||
} | ||
|
||
/** | ||
* Makes it easy to trigger validation on the control, that depends on | ||
* a value of a different control | ||
*/ | ||
export function dependentValidator<T = any>( | ||
opts: DependentValidatorOptions<T> | ||
) { | ||
let subscribed = false; | ||
|
||
return (formControl: AbstractControl) => { | ||
const form = formControl.root; | ||
const { watchControl, condition, validator } = opts; | ||
const controlToWatch = watchControl(form); | ||
|
||
if (!controlToWatch) { | ||
if (isDevMode()) { | ||
console.warn( | ||
`dependentValidator could not find specified watchControl` | ||
); | ||
} | ||
return null; | ||
} | ||
|
||
if (!subscribed) { | ||
subscribed = true; | ||
|
||
controlToWatch.valueChanges.subscribe(() => { | ||
formControl.updateValueAndValidity(); | ||
}); | ||
} | ||
|
||
if (condition === undefined || condition(controlToWatch.value)) { | ||
const validatorFn = validator(controlToWatch.value); | ||
return validatorFn(formControl); | ||
} | ||
|
||
return null; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters