Skip to content

Commit

Permalink
feat: Update to Angular 14 (#44)
Browse files Browse the repository at this point in the history
- Expose additional components due to Typescript requirement.
- Minor doco updates.
- Build pipeline bump to Node16 (from 14)

Co-authored-by: David Walschots <[email protected]>
  • Loading branch information
jafin and davidwalschots committed Dec 9, 2022
1 parent 44abdf6 commit 14ae930
Show file tree
Hide file tree
Showing 26 changed files with 11,507 additions and 9,909 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ testem.log
Thumbs.db

TESTS-*.xml
/.angular/cache/*
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 8.0.0

* add support for Angular 14
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Note that `formSubmitted` can be undefined when it's not known if the form is su

Angular provides a limited set of validator functions. To declare your own validator functions _and_ combine it with this library use the `ValidatorDeclaration` class. It supports declaring validators with zero, one or two arguments.

**Note** that if your validator doesn't return an object as the inner error result, but e.g. a `boolean` such as in the examples below, then this will be replaced by an object that can hold the validation message. Thus in the first example below `{ 'hasvalue': true }` becomes `{ 'hasvalue': { 'message': 'validation message' } }`.
**Note** that if your validator doesn't return an object as the inner error result, but e.g. a `boolean` such as in the examples below, then this will be replaced by an object that can hold the validation message. Thus, in the first example below `{ 'hasvalue': true }` becomes `{ 'hasvalue': { 'message': 'validation message' } }`.

```ts
const hasValueValidator = ValidatorDeclaration.wrapNoArgumentValidator(control => {
Expand Down
8 changes: 8 additions & 0 deletions angular-reactive-validation/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"dest": "../dist/angular-reactive-validation",
"lib": {
"entryFile": "src/public_api.ts"
},
"allowedNonPeerDependencies": ["."]
}
20 changes: 5 additions & 15 deletions angular-reactive-validation/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "angular-reactive-validation",
"description": "Reactive Forms validation shouldn't require the developer to write lots of HTML to show validation messages. This library makes it easy.",
"version": "7.0.0",
"version": "8.0.0",
"repository": {
"type": "git",
"url": "https://github.com/davidwalschots/angular-reactive-validation.git"
Expand All @@ -20,19 +20,9 @@
"private": false,
"dependencies": {},
"peerDependencies": {
"@angular/core": "^13.0.0",
"@angular/common": "^13.0.0",
"@angular/forms": "^13.0.0",
"rxjs": "^6.5.3"
},
"ngPackage": {
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"dest": "../dist/angular-reactive-validation",
"lib": {
"entryFile": "src/public_api.ts"
},
"allowedNonPeerDependencies": [
"."
]
"@angular/core": "^14.0.0",
"@angular/common": "^14.0.0",
"@angular/forms": "^14.0.0",
"rxjs": "^6.6.7"
}
}
4 changes: 2 additions & 2 deletions angular-reactive-validation/src/form/form.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { UntypedFormGroup, ReactiveFormsModule } from '@angular/forms';
import { TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

Expand Down Expand Up @@ -37,6 +37,6 @@ describe('FormDirective', () => {
</form>`
})
class TestHostComponent {
form = new FormGroup({});
form = new UntypedFormGroup({});
}
});
14 changes: 7 additions & 7 deletions angular-reactive-validation/src/get-control-path.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { TestBed, inject } from '@angular/core/testing';
import { FormBuilder } from '@angular/forms';
import { UntypedFormBuilder } from '@angular/forms';

import { getControlPath } from './get-control-path';

describe('getControlPath', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
FormBuilder
UntypedFormBuilder
]
});
});

it(`emits paths for form groups`, inject([FormBuilder], (fb: FormBuilder) => {
it(`emits paths for form groups`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const firstName = fb.control('');
fb.group({
name: fb.group({
Expand All @@ -23,7 +23,7 @@ describe('getControlPath', () => {
expect(getControlPath(firstName)).toEqual('name.firstName');
}));

it(`emits numeric paths for form arrays`, inject([FormBuilder], (fb: FormBuilder) => {
it(`emits numeric paths for form arrays`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const firstName = fb.control('');
const firstName2 = fb.control('');

Expand All @@ -42,21 +42,21 @@ describe('getControlPath', () => {
expect(getControlPath(firstName2)).toEqual('persons.1.firstName');
}));

it(`emits an empty string for a control without parents`, inject([FormBuilder], (fb: FormBuilder) => {
it(`emits an empty string for a control without parents`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const control = fb.control('');

expect(getControlPath(control)).toEqual('');
}));

it(`emits an index string for a control with only a form array as parent`, inject([FormBuilder], (fb: FormBuilder) => {
it(`emits an index string for a control with only a form array as parent`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const control = fb.control('');

fb.array([control]);

expect(getControlPath(control)).toEqual('0');
}));

it(`emits a single identifier for a control with only a single form group as parent`, inject([FormBuilder], (fb: FormBuilder) => {
it(`emits a single identifier for a control with only a single form group as parent`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const control = fb.control('');

fb.group({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { TestBed, inject } from '@angular/core/testing';
import { FormBuilder } from '@angular/forms';
import { UntypedFormBuilder } from '@angular/forms';

import { getFormControlFromContainer } from './get-form-control-from-container';

describe('getFormControlFromContainer', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
FormBuilder
UntypedFormBuilder
]
});
});

it(`gets a FormControl from the FormGroup`, inject([FormBuilder], (fb: FormBuilder) => {
it(`gets a FormControl from the FormGroup`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const firstName = fb.control('');
const group = fb.group({
firstName: firstName
Expand All @@ -25,13 +25,13 @@ describe('getFormControlFromContainer', () => {
expect(getFormControlFromContainer('firstName', container)).toBe(firstName);
}));

it(`throws an Error when no container is provided`, inject([FormBuilder], (fb: FormBuilder) => {
it(`throws an Error when no container is provided`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
expect(() => getFormControlFromContainer('firstName', undefined)).toThrow(new Error(
`You can't pass a string to arv-validation-messages's for attribute, when the ` +
`arv-validation-messages element is not a child of an element with a formGroupName or formGroup declaration.`));
}));

it(`throws an Error when there is no FormControl with the given name`, inject([FormBuilder], (fb: FormBuilder) => {
it(`throws an Error when there is no FormControl with the given name`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const group = fb.group({});

const container: any = {
Expand All @@ -44,7 +44,7 @@ describe('getFormControlFromContainer', () => {
));
}));

it(`throws an Error when there is a FormGroup with the given name`, inject([FormBuilder], (fb: FormBuilder) => {
it(`throws an Error when there is a FormGroup with the given name`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const group = fb.group({
name: fb.group({})
});
Expand All @@ -59,7 +59,7 @@ describe('getFormControlFromContainer', () => {
));
}));

it(`throws an Error when there is a FormArray with the given name`, inject([FormBuilder], (fb: FormBuilder) => {
it(`throws an Error when there is a FormArray with the given name`, inject([UntypedFormBuilder], (fb: UntypedFormBuilder) => {
const group = fb.group({
name: fb.array([])
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { FormGroup, FormControl, ControlContainer, FormGroupDirective } from '@angular/forms';
import { UntypedFormGroup, UntypedFormControl, ControlContainer, FormGroupDirective } from '@angular/forms';

export const getFormControlFromContainer = (name: string, controlContainer: ControlContainer | undefined): FormControl => {
export const getFormControlFromContainer = (name: string, controlContainer: ControlContainer | undefined): UntypedFormControl => {
if (controlContainer) {
const control = (controlContainer.control as FormGroup).controls[name];
const control = (controlContainer.control as UntypedFormGroup).controls[name];
if (!control) {
throw new Error(`There is no control named '${name}'` +
(getPath(controlContainer).length > 0 ? ` within '${getPath(controlContainer).join('.')}'` : '') + '.');
}
if (!(control instanceof FormControl)) {
if (!(control instanceof UntypedFormControl)) {
throw new Error(`The control named '${name}' ` +
(getPath(controlContainer).length > 0 ? `within '${getPath(controlContainer).join('.')}' ` : '') +
`is not a FormControl. Maybe you accidentally referenced a FormGroup or FormArray?`);
Expand Down
14 changes: 7 additions & 7 deletions angular-reactive-validation/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export {ReactiveValidationModule} from './reactive-validation.module';
export {ReactiveValidationModuleConfiguration} from './reactive-validation-module-configuration';
export {Validators} from './validators';
export {ValidatorDeclaration} from './validator-declaration';
export {ValidationMessageComponent} from './validation-message/validation-message.component';
export {FormDirective} from './form/form.directive';
export {ValidationMessagesComponent} from './validation-messages/validation-messages.component';
export { FormDirective } from './form/form.directive';
export { ReactiveValidationModule } from './reactive-validation.module';
export { ReactiveValidationModuleConfiguration } from './reactive-validation-module-configuration';
export { Validators } from './validators';
export { ValidatorDeclaration } from './validator-declaration';
export { ValidationMessagesComponent } from './validation-messages/validation-messages.component';
export { ValidationMessageComponent } from './validation-message/validation-message.component';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FormControl } from '@angular/forms';
import { UntypedFormControl } from '@angular/forms';

export interface ReactiveValidationModuleConfiguration {
/**
Expand All @@ -9,5 +9,5 @@ export interface ReactiveValidationModuleConfiguration {
* @param formSubmitted whether the form is submitted or not. When undefined, it's not known
* if the form is submitted, due to the form tag missing a formGroup.
*/
displayValidationMessageWhen?: (control: FormControl, formSubmitted: boolean | undefined) => boolean;
displayValidationMessageWhen?: (control: UntypedFormControl, formSubmitted: boolean | undefined) => boolean;
}
8 changes: 4 additions & 4 deletions angular-reactive-validation/src/validation-error.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { FormControl, ValidationErrors } from '@angular/forms';
import { UntypedFormControl, ValidationErrors } from '@angular/forms';

export class ValidationError {
control: FormControl;
control: UntypedFormControl;
key: string;
errorObject: ValidationErrors;

constructor(control: FormControl, key: string, errorObject: ValidationErrors) {
constructor(control: UntypedFormControl, key: string, errorObject: ValidationErrors) {
this.control = control;
this.key = key;
this.errorObject = errorObject;
}

static fromFirstError(control: FormControl): ValidationError | undefined {
static fromFirstError(control: UntypedFormControl): ValidationError | undefined {
if (!control.errors) {
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing';
import { ValidationMessageComponent } from './validation-message.component';
import { ValidationError } from '../validation-error';
import { Validators } from '../validators';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { UntypedFormControl, UntypedFormGroup, ReactiveFormsModule } from '@angular/forms';

describe('ValidationMessageComponent', () => {
describe('canHandle', () => {
Expand Down Expand Up @@ -130,10 +130,10 @@ describe('ValidationMessageComponent', () => {
class TestHostComponent {
@ViewChild(ValidationMessageComponent, { static: true }) validationMessageComponent: ValidationMessageComponent;

age = new FormControl(0, [
age = new UntypedFormControl(0, [
Validators.min(10, 'invalid age')
]);
form = new FormGroup({
form = new UntypedFormGroup({
age: this.age
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, Input, ViewEncapsulation, Optional, OnInit } from '@angular/core';
import { FormControl, ValidationErrors, ControlContainer } from '@angular/forms';
import { UntypedFormControl, ValidationErrors, ControlContainer } from '@angular/forms';

import { ValidationError } from '../validation-error';
import { getFormControlFromContainer, isControlContainerVoidOrInitialized } from '../get-form-control-from-container';
Expand All @@ -22,14 +22,14 @@ export class ValidationMessageComponent implements OnInit {
* The FormControl for which a custom validation message should be shown. This is only required when the parent
* ValidationMessagesComponent has multiple FormControls specified.
*/
set for(control: FormControl | string | undefined) {
set for(control: UntypedFormControl | string | undefined) {
if (!isControlContainerVoidOrInitialized(this.controlContainer)) {
this.initializeForOnInit = () => this.for = control;
return;
}
this._for = typeof control === 'string' ? getFormControlFromContainer(control, this.controlContainer) : control;
}
get for(): FormControl | string | undefined {
get for(): UntypedFormControl | string | undefined {
return this._for;
}

Expand All @@ -40,7 +40,7 @@ export class ValidationMessageComponent implements OnInit {
key: string | undefined;

private _context: ValidationErrors | undefined;
private _for: FormControl | undefined;
private _for: UntypedFormControl | undefined;

constructor(@Optional() private controlContainer: ControlContainer) { }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, ViewChild } from '@angular/core';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { ControlContainer, FormGroup, FormControl, ReactiveFormsModule, FormGroupDirective } from '@angular/forms';
import { ControlContainer, UntypedFormGroup, ReactiveFormsModule, FormGroupDirective, FormControl, FormGroup, UntypedFormControl }
from '@angular/forms';
import { Subject } from 'rxjs';

import { ValidationMessagesComponent } from './validation-messages.component';
Expand All @@ -14,7 +15,7 @@ const isErrorEvent = (event: Event | string): event is ErrorEvent => (event as E
describe('ValidationMessagesComponent', () => {
describe('properties and functions', () => {
let component: ValidationMessagesComponent;
let formGroup: FormGroup;
let formGroup: UntypedFormGroup;
let firstNameControl: FormControl;
let middleNameControl: FormControl;
let lastNameControl: FormControl;
Expand All @@ -29,7 +30,7 @@ describe('ValidationMessagesComponent', () => {
Validators.required('A last name is required'),
Validators.minLength(5, minLength => `Last name needs to be at least ${minLength} characters long`)
]);
formGroup = new FormGroup({
formGroup = new UntypedFormGroup({
firstName: firstNameControl,
middleName: middleNameControl,
lastName: lastNameControl
Expand Down Expand Up @@ -83,7 +84,7 @@ describe('ValidationMessagesComponent', () => {
it(`getErrorMessages returns the first error message per touched control (default configuration)`, () => {
component.for = [firstNameControl, middleNameControl, lastNameControl];
firstNameControl.markAsTouched();
// We skip middleNameControl on purpose, to ensure that it doesn't return it's error.
// We skip middleNameControl on purpose, to ensure that it doesn't return its error.
lastNameControl.markAsTouched();
lastNameControl.setValue('abc');

Expand Down Expand Up @@ -131,7 +132,7 @@ describe('ValidationMessagesComponent', () => {

describe('an alternative configuration', () => {
const configuration = {
displayValidationMessageWhen: () => true
displayValidationMessageWhen: (_: UntypedFormControl, __: boolean | undefined) => true
};

beforeEach(() => {
Expand All @@ -155,7 +156,7 @@ describe('ValidationMessagesComponent', () => {

it(`displayValidationMessageWhen's formSubmitted is undefined when a FormDirective is not provided`, () => {
fixture.detectChanges();
expect(configuration.displayValidationMessageWhen).toHaveBeenCalledWith(jasmine.any(FormControl), undefined);
expect(configuration.displayValidationMessageWhen).toHaveBeenCalledWith(jasmine.any(UntypedFormControl), undefined);
});
});

Expand Down
Loading

0 comments on commit 14ae930

Please sign in to comment.