Skip to content

Commit

Permalink
feat: upgrade to Angular 15
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Angular 15 required

This closes #282
  • Loading branch information
maxime1992 committed Dec 9, 2022
1 parent 81920bd commit 7890724
Show file tree
Hide file tree
Showing 16 changed files with 2,441 additions and 1,974 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ npm i ngx-sub-form
| `13.x` | `5.2.0` (non breaking but new API available as well) |
| `14.x` | `6.0.0` (Angular 14 upgrade only) |
| `14.x` | `7.0.0` (deprecated API is now removed) |
| `15.x` | `8.0.0` |

# API

Expand Down
8 changes: 8 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,13 @@
},
"cli": {
"schematicCollections": ["@angular-eslint/schematics"]
},
"schematics": {
"@angular-eslint/schematics:application": {
"setParserOptionsProject": true
},
"@angular-eslint/schematics:library": {
"setParserOptionsProject": true
}
}
}
76 changes: 38 additions & 38 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,67 +36,67 @@
},
"private": true,
"dependencies": {
"@angular/animations": "14.2.2",
"@angular/cdk": "14.0.6",
"@angular/common": "14.2.2",
"@angular/compiler": "14.2.2",
"@angular/core": "14.2.2",
"@angular/forms": "14.2.2",
"@angular/material": "14.0.6",
"@angular/platform-browser": "14.2.2",
"@angular/platform-browser-dynamic": "14.2.2",
"@angular/router": "14.2.2",
"@types/uuid": "8.3.4",
"commitizen": "4.2.4",
"@angular/animations": "15.0.2",
"@angular/cdk": "15.0.1",
"@angular/common": "15.0.2",
"@angular/compiler": "15.0.2",
"@angular/core": "15.0.2",
"@angular/forms": "15.0.2",
"@angular/material": "15.0.1",
"@angular/platform-browser": "15.0.2",
"@angular/platform-browser-dynamic": "15.0.2",
"@angular/router": "15.0.2",
"@types/uuid": "9.0.0",
"commitizen": "4.2.6",
"core-js": "3.23.1",
"fast-deep-equal": "3.1.3",
"lodash-es": "4.17.21",
"ngx-observable-lifecycle": "2.2.1",
"rxjs": "7.5.5",
"tslib": "2.4.0",
"uuid": "8.3.2",
"rxjs": "7.6.0",
"tslib": "2.4.1",
"uuid": "9.0.0",
"zone.js": "0.11.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "14.2.3",
"@angular-eslint/builder": "14.1.1",
"@angular-eslint/eslint-plugin": "14.1.1",
"@angular-eslint/eslint-plugin-template": "14.1.1",
"@angular-eslint/schematics": "14.1.1",
"@angular-eslint/template-parser": "14.1.1",
"@angular/cli": "14.2.3",
"@angular/compiler-cli": "14.2.2",
"@angular/language-service": "14.2.2",
"@types/jasmine": "4.0.3",
"@angular-devkit/build-angular": "15.0.2",
"@angular-eslint/builder": "15.1.0",
"@angular-eslint/eslint-plugin": "15.1.0",
"@angular-eslint/eslint-plugin-template": "15.1.0",
"@angular-eslint/schematics": "15.1.0",
"@angular-eslint/template-parser": "15.1.0",
"@angular/cli": "15.0.2",
"@angular/compiler-cli": "15.0.2",
"@angular/language-service": "15.0.2",
"@types/jasmine": "4.3.1",
"@types/jasminewd2": "2.0.10",
"@types/lodash": "4.14.182",
"@types/lodash": "4.14.191",
"@types/lodash-es": "4.17.6",
"@types/node": "17.0.40",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"@types/node": "18.11.11",
"@typescript-eslint/eslint-plugin": "5.45.1",
"@typescript-eslint/parser": "5.45.1",
"cypress": "10.1.0",
"cz-conventional-changelog": "3.3.0",
"embedme": "1.22.0",
"eslint": "^8.23.0",
"embedme": "1.22.1",
"eslint": "8.29.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jsdoc": "39.3.3",
"eslint-plugin-jsdoc": "39.6.4",
"eslint-plugin-prefer-arrow": "1.2.3",
"http-server-spa": "1.3.0",
"jasmine-core": "4.2.0",
"jasmine-core": "4.5.0",
"jasmine-spec-reporter": "7.0.0",
"karma": "6.4.0",
"karma": "6.4.1",
"karma-chrome-launcher": "3.1.1",
"karma-coverage-istanbul-reporter": "3.0.3",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.0.0",
"ng-packagr": "14.0.4",
"ng-packagr": "15.0.1",
"prettier": "2.7.1",
"semantic-release": "19.0.3",
"ts-node": "10.8.1",
"semantic-release": "19.0.5",
"ts-node": "10.9.1",
"tsconfig-paths-webpack-plugin": "3.5.2",
"tsdef": "0.0.14",
"typescript": "4.7.4"
"typescript": "4.8.4"
},
"repository": {
"type": "git",
Expand Down
8 changes: 4 additions & 4 deletions projects/ngx-sub-form/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
},
"peerDependencies": {
"fast-deep-equal": "^3.1.3",
"@angular/common": "^14.0.0",
"@angular/core": "^14.0.0",
"@angular/forms": "^14.0.0",
"@angular/common": "^15.0.0",
"@angular/core": "^15.0.0",
"@angular/forms": "^15.0.0",
"ngx-observable-lifecycle": "^2.2.1",
"rxjs": "^7.5.5",
"rxjs": "^7.6.0",
"tsdef": "0.0.14"
},
"keywords": [
Expand Down
16 changes: 11 additions & 5 deletions projects/ngx-sub-form/src/lib/create-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,34 @@ import {
} from './ngx-sub-form.types';
import { isNullOrUndefined } from './shared/ngx-sub-form-utils';

const optionsHaveInstructionsToCreateArrays = <ControlInterface, FormInterface>(
const optionsHaveInstructionsToCreateArrays = <ControlInterface, FormInterface extends {}>(
options: NgxFormOptions<ControlInterface, FormInterface> & Partial<NgxSubFormArrayOptions<FormInterface>>,
): options is NgxSubFormOptions<ControlInterface, FormInterface> & NgxSubFormArrayOptions<FormInterface> =>
!!options.createFormArrayControl;

// @todo find a better name
const isRoot = <ControlInterface, FormInterface>(
const isRoot = <ControlInterface, FormInterface extends {}>(
options: any,
): options is NgxRootFormOptions<ControlInterface, FormInterface> => {
const opt = options as NgxRootFormOptions<ControlInterface, FormInterface>;
return opt.formType === FormType.ROOT;
};

export function createForm<ControlInterface, FormInterface = ControlInterface>(
export function createForm<
ControlInterface,
FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never,
>(
componentInstance: ControlValueAccessorComponentInstance,
options: NgxRootFormOptions<ControlInterface, FormInterface>,
): NgxRootForm<ControlInterface, FormInterface>;
export function createForm<ControlInterface, FormInterface = ControlInterface>(
export function createForm<
ControlInterface,
FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never,
>(
componentInstance: ControlValueAccessorComponentInstance,
options: NgxSubFormOptions<ControlInterface, FormInterface>,
): NgxSubForm<ControlInterface, FormInterface>;
export function createForm<ControlInterface, FormInterface>(
export function createForm<ControlInterface, FormInterface extends {}>(
componentInstance: ControlValueAccessorComponentInstance,
options: NgxFormOptions<ControlInterface, FormInterface>,
): NgxSubForm<ControlInterface, FormInterface> {
Expand Down
6 changes: 3 additions & 3 deletions projects/ngx-sub-form/src/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
UntypedFormGroup,
ValidationErrors,
} from '@angular/forms';
import { cloneDeep } from 'lodash-es';
import { ReplaySubject } from 'rxjs';
import { Nilable } from 'tsdef';
import {
Expand All @@ -20,7 +21,6 @@ import {
OneOfControlsTypes,
TypedFormGroup,
} from './shared/ngx-sub-form-utils';
import { cloneDeep } from 'lodash-es';

/** @internal */
export const patchClassInstance = (componentInstance: any, obj: Object) => {
Expand Down Expand Up @@ -63,7 +63,7 @@ export const getControlValueAccessorBindings = <ControlInterface>(
};
};

export const getFormGroupErrors = <ControlInterface, FormInterface>(
export const getFormGroupErrors = <ControlInterface, FormInterface extends {}>(
formGroup: TypedFormGroup<FormInterface>,
): NewFormErrors<FormInterface> => {
const formErrors: NewFormErrors<ControlInterface> = Object.entries<OneOfControlsTypes>(formGroup.controls).reduce<
Expand Down Expand Up @@ -114,7 +114,7 @@ interface FormArrayWrapper<FormInterface> {
control: UntypedFormArray;
}

export function createFormDataFromOptions<ControlInterface, FormInterface>(
export function createFormDataFromOptions<ControlInterface, FormInterface extends {}>(
options: NgxSubFormOptions<ControlInterface, FormInterface>,
) {
const formGroup: TypedFormGroup<FormInterface> = new UntypedFormGroup(
Expand Down
18 changes: 11 additions & 7 deletions projects/ngx-sub-form/src/lib/ngx-sub-form.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type ControlValueAccessorComponentInstance = Object &
// and this should *never* be overridden by the component
Partial<Record<keyof ControlValueAccessor, never> & Record<keyof Validator, never>>;

export interface NgxSubForm<ControlInterface, FormInterface> {
export interface NgxSubForm<ControlInterface, FormInterface extends {}> {
readonly formGroup: TypedFormGroup<FormInterface>;
readonly formControlNames: ControlsNames<FormInterface>;
readonly formGroupErrors: NewFormErrors<FormInterface>;
Expand All @@ -43,7 +43,8 @@ export type CreateFormArrayControlMethod<FormInterface> = <K extends ArrayProper
initialValue: ArrayPropertyValue<FormInterface, K>,
) => UntypedFormControl;

export interface NgxRootForm<ControlInterface, FormInterface> extends NgxSubForm<ControlInterface, FormInterface> {
export interface NgxRootForm<ControlInterface, FormInterface extends {}>
extends NgxSubForm<ControlInterface, FormInterface> {
// @todo: anything else needed here?
}

Expand All @@ -69,7 +70,10 @@ type NgxSubFormArray<FormInterface> = ArrayPropertyKey<FormInterface> extends ne
? {} // no point defining `createFormArrayControl` if there's not a single array in the `FormInterface`
: NgxSubFormArrayOptions<FormInterface>;

export type NgxSubFormOptions<ControlInterface, FormInterface = ControlInterface> = {
export type NgxSubFormOptions<
ControlInterface,
FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never,
> = {
formType: FormType;
formControls: Controls<FormInterface>;
formGroupOptions?: FormGroupOptions<FormInterface>;
Expand All @@ -80,10 +84,10 @@ export type NgxSubFormOptions<ControlInterface, FormInterface = ControlInterface
} & NgxSubFormRemap<ControlInterface, FormInterface> &
NgxSubFormArray<FormInterface>;

export type NgxRootFormOptions<ControlInterface, FormInterface = ControlInterface> = NgxSubFormOptions<
export type NgxRootFormOptions<
ControlInterface,
FormInterface
> & {
FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never,
> = NgxSubFormOptions<ControlInterface, FormInterface> & {
input$: Observable<ControlInterface | undefined>;
output$: Subject<ControlInterface>;
disabled$?: Observable<boolean>;
Expand All @@ -105,6 +109,6 @@ export enum FormType {
ROOT = 'Root',
}

export type NgxFormOptions<ControlInterface, FormInterface> =
export type NgxFormOptions<ControlInterface, FormInterface extends {}> =
| NgxSubFormOptions<ControlInterface, FormInterface>
| NgxRootFormOptions<ControlInterface, FormInterface>;
14 changes: 8 additions & 6 deletions projects/ngx-sub-form/src/lib/shared/ngx-sub-form-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { InjectionToken, Type } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
UntypedFormArray,
UntypedFormControl,
UntypedFormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
} from '@angular/forms';
import { getObservableLifecycle } from 'ngx-observable-lifecycle';
import { Observable, Subject, timer } from 'rxjs';
import { Observable, timer } from 'rxjs';
import { debounce, takeUntil } from 'rxjs/operators';

export type Controls<T> = { [K in keyof T]-?: AbstractControl };
Expand All @@ -20,7 +20,9 @@ export type ControlsNames<T> = { [K in keyof T]-?: K };
export type ControlMap<T, V> = { [K in keyof T]-?: V };

export type ControlsType<T> = {
[K in keyof T]-?: T[K] extends any[] ? TypedFormArray<T[K]> : TypedFormControl<T[K]> | TypedFormGroup<T[K]>;
[K in keyof T]-?: T[K] extends any[]
? TypedFormArray<T[K]>
: TypedFormControl<T[K]> | (T[K] extends {} ? TypedFormGroup<T[K]> : never);
};

export type OneOfControlsTypes<T = any> = ControlsType<T>[keyof ControlsType<T>];
Expand All @@ -45,7 +47,7 @@ export interface TypedAbstractControl<TValue> extends AbstractControl {
patchValue(value: Partial<TValue>, options?: Parameters<AbstractControl['patchValue']>[1]): void;
}

export interface TypedFormGroup<TValue> extends UntypedFormGroup {
export interface TypedFormGroup<TValue extends {}> extends UntypedFormGroup {
value: TValue;
valueChanges: Observable<TValue>;
controls: ControlsType<TValue>;
Expand All @@ -63,7 +65,7 @@ export interface TypedFormArray<TValue extends any[]> extends UntypedFormArray {
getRawValue(): TValue;
}

export interface TypedFormControl<TValue> extends UntypedFormGroup {
export interface TypedFormControl<TValue> extends UntypedFormControl {
value: TValue;
valueChanges: Observable<TValue>;
setValue(value: TValue, options?: Parameters<UntypedFormControl['setValue']>[1]): void;
Expand Down
6 changes: 3 additions & 3 deletions projects/ngx-sub-form/src/lib/shared/ngx-sub-form.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ type Nullable<T> = T | null;

export type NullableObject<T> = { [P in keyof T]: Nullable<T[P]> };

export type TypedValidatorFn<T> = (formGroup: TypedFormGroup<T>) => ValidationErrors | null;
export type TypedValidatorFn<T extends {}> = (formGroup: TypedFormGroup<T>) => ValidationErrors | null;

export type TypedAsyncValidatorFn<T> = (
export type TypedAsyncValidatorFn<T extends {}> = (
formGroup: TypedFormGroup<T>,
) => Promise<ValidationErrors | null> | Observable<ValidationErrors | null>;

export interface FormGroupOptions<T> {
export interface FormGroupOptions<T extends {}> {
/**
* @description The list of validators applied to a control.
*/
Expand Down
6 changes: 0 additions & 6 deletions projects/ngx-sub-form/src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import 'core-js/proposals/reflect-metadata';

declare const require: any;

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false },
});
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
1 change: 0 additions & 1 deletion projects/ngx-sub-form/tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"declarationMap": true,
"target": "es2020",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
Expand Down
15 changes: 6 additions & 9 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@ import { SharedModule } from './shared/shared.module';
imports: [
BrowserModule,
BrowserAnimationsModule,
RouterModule.forRoot(
[
{
path: '',
loadChildren: () => import('./main/main.module').then(x => x.MainModule),
},
],
{ relativeLinkResolution: 'legacy' },
),
RouterModule.forRoot([
{
path: '',
loadChildren: () => import('./main/main.module').then(x => x.MainModule),
},
]),
SharedModule,
],
providers: [],
Expand Down
Loading

0 comments on commit 7890724

Please sign in to comment.