diff --git a/.github/LICENSE b/.github/LICENSE index 51199d6..00550a8 100644 --- a/.github/LICENSE +++ b/.github/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2023 hackingharold +Copyright (c) 2024 hackingharold Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml index 9d14210..6d1c398 100644 --- a/.github/workflows/on-release.yml +++ b/.github/workflows/on-release.yml @@ -14,10 +14,11 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 registry-url: https://registry.npmjs.org/ always-auth: true token: ${{ secrets.NPM_TOKEN }} + - uses: oven-sh/setup-bun@v1 - run: bun install --frozen-lockfile diff --git a/LICENSE b/LICENSE index b762871..a00c951 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 hackingharold +Copyright (c) 2024 hackingharold Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bun.lockb b/bun.lockb index bd2920b..fcbb656 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 801a6bc..6b36c81 100644 --- a/package.json +++ b/package.json @@ -14,42 +14,42 @@ "lint:material": "ng lint material" }, "dependencies": { - "@angular/animations": "^18.2.8", - "@angular/cdk": "~18.2.9", - "@angular/common": "^18.2.8", - "@angular/compiler": "^18.2.8", - "@angular/core": "^18.2.8", - "@angular/forms": "^18.2.8", - "@angular/material": "~18.2.9", - "@angular/platform-browser": "^18.2.8", - "@angular/platform-browser-dynamic": "^18.2.8", - "@angular/router": "^18.2.8", + "@angular/animations": "^19.0.0", + "@angular/cdk": "~19.0.0", + "@angular/common": "^19.0.0", + "@angular/compiler": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/forms": "^19.0.0", + "@angular/material": "~19.0.0", + "@angular/platform-browser": "^19.0.0", + "@angular/platform-browser-dynamic": "^19.0.0", + "@angular/router": "^19.0.0", "rxjs": "~7.8.1", - "tslib": "^2.8.0", + "tslib": "^2.8.1", "zone.js": "^0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.9", - "@angular-eslint/builder": "^18.4.0", - "@angular-eslint/eslint-plugin": "18.4.0", - "@angular-eslint/eslint-plugin-template": "18.4.0", - "@angular-eslint/schematics": "^18.4.0", - "@angular-eslint/template-parser": "18.4.0", - "@angular/cli": "^18.2.9", - "@angular/compiler-cli": "^18.2.8", + "@angular-devkit/build-angular": "^19.0.1", + "@angular-eslint/builder": "^18.4.1", + "@angular-eslint/eslint-plugin": "18.4.1", + "@angular-eslint/eslint-plugin-template": "18.4.1", + "@angular-eslint/schematics": "^18.4.1", + "@angular-eslint/template-parser": "18.4.1", + "@angular/cli": "^19.0.1", + "@angular/compiler-cli": "^19.0.0", "@types/jasmine": "~5.1.4", - "@types/node": "^22.7.6", - "@typescript-eslint/eslint-plugin": "^8.10.0", - "@typescript-eslint/parser": "^8.10.0", + "@types/node": "^22.9.1", + "@typescript-eslint/eslint-plugin": "^8.15.0", + "@typescript-eslint/parser": "^8.15.0", "ajv": "^8.17.1", - "eslint": "^9.12.0", + "eslint": "^9.15.0", "jasmine-core": "~5.4.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.1", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", - "ng-packagr": "^18.2.1", - "typescript": "5.5.4" + "ng-packagr": "^19.0.0", + "typescript": "5.6.3" } } diff --git a/projects/app/src/app/app.component.ts b/projects/app/src/app/app.component.ts index 7ed3370..b0cdfc0 100644 --- a/projects/app/src/app/app.component.ts +++ b/projects/app/src/app/app.component.ts @@ -1,8 +1,27 @@ +import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { FormControl } from '@angular/forms'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatChipRow } from '@angular/material/chips'; +import { MatError, MatFormField, MatLabel } from '@angular/material/form-field'; +import { MatIcon } from '@angular/material/icon'; +import { MatInput } from '@angular/material/input'; +import { FileInputDirective } from '@ngx-dropzone/cdk'; +import { MatDropzone } from '@ngx-dropzone/material'; @Component({ selector: 'app-root', + imports: [ + CommonModule, + ReactiveFormsModule, + MatFormField, + MatLabel, + MatError, + MatInput, + MatChipRow, + MatIcon, + MatDropzone, + FileInputDirective, + ], template: `
Drop only .png files! diff --git a/projects/app/src/app/app.module.ts b/projects/app/src/app/app.module.ts deleted file mode 100644 index 6f41c97..0000000 --- a/projects/app/src/app/app.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { DropzoneCdkModule } from '@ngx-dropzone/cdk'; -import { DropzoneMaterialModule } from '@ngx-dropzone/material'; -import { AppComponent } from './app.component'; - -@NgModule({ - declarations: [AppComponent], - imports: [ - BrowserModule, - BrowserAnimationsModule, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatChipsModule, - MatIconModule, - DropzoneCdkModule, - DropzoneMaterialModule, - ], - providers: [], - bootstrap: [AppComponent], -}) -export class AppModule {} diff --git a/projects/app/src/main.ts b/projects/app/src/main.ts index c58dc05..c67d684 100644 --- a/projects/app/src/main.ts +++ b/projects/app/src/main.ts @@ -1,7 +1,7 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { AppComponent } from './app/app.component'; -import { AppModule } from './app/app.module'; - - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +bootstrapApplication(AppComponent, { + providers: [provideAnimations()], +}).catch((e) => console.error(e)); diff --git a/projects/cdk/package.json b/projects/cdk/package.json index 7d86d05..7ec7e54 100644 --- a/projects/cdk/package.json +++ b/projects/cdk/package.json @@ -23,9 +23,9 @@ "cdk" ], "peerDependencies": { - "@angular/common": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/forms": "^18.0.0", + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/forms": "^19.0.0", "rxjs": "^7.4.0" } } diff --git a/projects/cdk/src/lib/cdk.module.ts b/projects/cdk/src/lib/cdk.module.ts deleted file mode 100644 index 4d5d24e..0000000 --- a/projects/cdk/src/lib/cdk.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NgModule } from '@angular/core'; -import { DropzoneComponent } from './dropzone'; -import { FileInputDirective } from './file-input'; - -@NgModule({ - declarations: [FileInputDirective, DropzoneComponent], - exports: [FileInputDirective, DropzoneComponent], -}) -export class DropzoneCdkModule {} diff --git a/projects/cdk/src/lib/dropzone/dropzone.component.spec.ts b/projects/cdk/src/lib/dropzone/dropzone.component.spec.ts index 0ceec36..8139af9 100644 --- a/projects/cdk/src/lib/dropzone/dropzone.component.spec.ts +++ b/projects/cdk/src/lib/dropzone/dropzone.component.spec.ts @@ -3,7 +3,6 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { FileInputDirective, FileInputValidators, FileInputValue } from '../file-input'; -import { DropzoneCdkModule } from './../cdk.module'; import { DropzoneComponent } from './dropzone.component'; interface Selectors { @@ -17,8 +16,7 @@ interface Selectors { describe('DropzoneComponent', () => { function configureDropzoneTestingModule(testComponent: Type): Selectors { const fixture = TestBed.configureTestingModule({ - imports: [ReactiveFormsModule, DropzoneCdkModule], - declarations: [testComponent], + imports: [testComponent], }).createComponent(testComponent); const element = fixture.debugElement.query(By.directive(DropzoneComponent)); @@ -148,6 +146,7 @@ describe('DropzoneComponent', () => { @Component({ selector: 'basic-dropzone', + imports: [DropzoneComponent, FileInputDirective], template: ` @@ -158,6 +157,7 @@ class DropzoneBasic {} @Component({ selector: 'form-control-dropzone', + imports: [ReactiveFormsModule, DropzoneComponent, FileInputDirective], template: ` diff --git a/projects/cdk/src/lib/dropzone/dropzone.component.ts b/projects/cdk/src/lib/dropzone/dropzone.component.ts index 2c4e487..95eef11 100644 --- a/projects/cdk/src/lib/dropzone/dropzone.component.ts +++ b/projects/cdk/src/lib/dropzone/dropzone.component.ts @@ -20,6 +20,8 @@ import { DropzoneService } from './dropzone.service'; @Component({ selector: 'ngx-dropzone', exportAs: 'dropzone', + imports: [FileInputDirective], + providers: [DropzoneService], template: ``, host: { tabindex: '0', diff --git a/projects/cdk/src/lib/dropzone/dropzone.service.ts b/projects/cdk/src/lib/dropzone/dropzone.service.ts index 3d7cc2e..ead22ce 100644 --- a/projects/cdk/src/lib/dropzone/dropzone.service.ts +++ b/projects/cdk/src/lib/dropzone/dropzone.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { nonNullable } from '../coercion'; -import { File } from './../file-input'; +import { type File } from './../file-input'; @Injectable({ providedIn: 'root', diff --git a/projects/cdk/src/lib/file-input/accept.service.spec.ts b/projects/cdk/src/lib/file-input/accept.service.spec.ts index e209e17..46e412e 100644 --- a/projects/cdk/src/lib/file-input/accept.service.spec.ts +++ b/projects/cdk/src/lib/file-input/accept.service.spec.ts @@ -4,6 +4,11 @@ import { AcceptService } from './accept.service'; describe('AcceptService', () => { let service: AcceptService; + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AcceptService); + }); + /** Returns a simple fake file with given extension and optional type. */ const getFile = (ext: string, type?: string) => new File(['...'], `${Date.now()}.${ext}`, { type }); @@ -14,11 +19,6 @@ describe('AcceptService', () => { getFile('pdf', 'application/octet-stream'), ]; - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AcceptService); - }); - it('should filter based on file extension', () => { const files = getFileList(); const accepted = service.accepts(files, '.doc, txt , .pdf,random/MIME'); diff --git a/projects/cdk/src/lib/file-input/file-input.directive.spec.ts b/projects/cdk/src/lib/file-input/file-input.directive.spec.ts index c8b4c2f..8c84bb1 100644 --- a/projects/cdk/src/lib/file-input/file-input.directive.spec.ts +++ b/projects/cdk/src/lib/file-input/file-input.directive.spec.ts @@ -1,6 +1,6 @@ import { Component, DebugElement, Type } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { getArrayValueError, getNonArrayValueError } from './file-input-errors'; import { FileInputValue } from './file-input-value'; @@ -24,8 +24,7 @@ const getFileList = (files: File[]): FileList => { describe('FileInputDirective', () => { function configureFileInputTestingModule(testComponent: Type): Selectors { const fixture = TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule], - declarations: [FileInputDirective, testComponent], + imports: [testComponent], }).createComponent(testComponent); const inputElement = fixture.debugElement.query(By.directive(FileInputDirective)); @@ -299,27 +298,32 @@ describe('FileInputDirective', () => { }); @Component({ + imports: [FileInputDirective], template: ``, }) class FileInputBasic {} @Component({ + imports: [FileInputDirective], template: ``, }) class FileInputMultiple {} @Component({ + imports: [FileInputDirective], // This combination is not valid! "Append" should only be used together with "multiple". template: ``, }) class FileInputAppend {} @Component({ + imports: [FileInputDirective], template: ``, }) class FileInputMultipleAppend {} @Component({ + imports: [ReactiveFormsModule, FileInputDirective], template: ``, }) class FileInputWithFormControl { @@ -327,6 +331,7 @@ class FileInputWithFormControl { } @Component({ + imports: [ReactiveFormsModule, FileInputDirective], template: ``, }) class FileInputDisabled {} diff --git a/projects/cdk/src/public-api.ts b/projects/cdk/src/public-api.ts index 12f88a2..eecfc72 100644 --- a/projects/cdk/src/public-api.ts +++ b/projects/cdk/src/public-api.ts @@ -2,7 +2,6 @@ * Public API Surface of cdk */ -export * from './lib/cdk.module'; export * from './lib/coercion'; export * from './lib/dropzone'; export * from './lib/file-input'; diff --git a/projects/material/package.json b/projects/material/package.json index b164607..20af854 100644 --- a/projects/material/package.json +++ b/projects/material/package.json @@ -23,10 +23,10 @@ "material" ], "peerDependencies": { - "@angular/common": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/forms": "^18.0.0", - "@ngx-dropzone/cdk": "^18.0.0", + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/forms": "^19.0.0", + "@ngx-dropzone/cdk": "^19.0.0", "rxjs": "^7.4.0" } } diff --git a/projects/material/src/lib/mat-dropzone/mat-dropzone.component.spec.ts b/projects/material/src/lib/mat-dropzone/mat-dropzone.component.spec.ts index a6ba8f6..10b6688 100644 --- a/projects/material/src/lib/mat-dropzone/mat-dropzone.component.spec.ts +++ b/projects/material/src/lib/mat-dropzone/mat-dropzone.component.spec.ts @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { Component, DebugElement, Type } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; @@ -6,8 +7,7 @@ import { MatError, MatFormFieldModule, MatLabel } from '@angular/material/form-f import { MatIconModule } from '@angular/material/icon'; import { By } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { DropzoneCdkModule, FileInputDirective, FileInputValidators, FileInputValue } from '@ngx-dropzone/cdk'; -import { DropzoneMaterialModule } from '../material.module'; +import { FileInputDirective, FileInputValidators, FileInputValue } from '@ngx-dropzone/cdk'; import { MatDropzone } from './mat-dropzone.component'; interface Selectors { @@ -25,16 +25,7 @@ interface Selectors { describe('MatDropzone', () => { function configureDropzoneTestingModule(testComponent: Type): Selectors { const fixture = TestBed.configureTestingModule({ - imports: [ - BrowserAnimationsModule, - ReactiveFormsModule, - MatFormFieldModule, - MatIconModule, - MatChipsModule, - DropzoneCdkModule, - DropzoneMaterialModule, - ], - declarations: [testComponent], + imports: [CommonModule, BrowserAnimationsModule, testComponent], }).createComponent(testComponent); const element = fixture.debugElement.query(By.directive(MatDropzone)); @@ -127,6 +118,7 @@ describe('MatDropzone', () => { @Component({ selector: 'basic-dropzone', + imports: [MatFormFieldModule, MatDropzone, FileInputDirective], template: ` Drop it basic! @@ -140,6 +132,7 @@ class DropzoneBasic {} @Component({ selector: 'form-control-dropzone', + imports: [ReactiveFormsModule, MatFormFieldModule, MatIconModule, MatChipsModule, MatDropzone, FileInputDirective], template: ` diff --git a/projects/material/src/lib/mat-dropzone/mat-dropzone.component.ts b/projects/material/src/lib/mat-dropzone/mat-dropzone.component.ts index d941ee5..5d11b4c 100644 --- a/projects/material/src/lib/mat-dropzone/mat-dropzone.component.ts +++ b/projects/material/src/lib/mat-dropzone/mat-dropzone.component.ts @@ -14,14 +14,31 @@ import { import { Validators } from '@angular/forms'; import { MatChipRow } from '@angular/material/chips'; import { MatFormField, MatFormFieldControl } from '@angular/material/form-field'; -import { coerceBoolean, DropzoneComponent, FileInputValue } from '@ngx-dropzone/cdk'; +import { + AcceptService, + coerceBoolean, + DropzoneComponent, + DropzoneService, + FileInputDirective, + FileInputValue, +} from '@ngx-dropzone/cdk'; import { merge, Observable, Subject, takeUntil, tap } from 'rxjs'; @Component({ selector: 'ngx-mat-dropzone', exportAs: 'matDropzone', + imports: [MatFormField, MatChipRow, FileInputDirective, DropzoneComponent], + providers: [ + DropzoneService, + AcceptService, + { + provide: MatFormFieldControl, + useExisting: MatDropzone, + }, + ], template: ` +
@@ -57,12 +74,6 @@ import { merge, Observable, Subject, takeUntil, tap } from 'rxjs'; ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: MatFormFieldControl, - useExisting: MatDropzone, - }, - ], }) export class MatDropzone extends DropzoneComponent diff --git a/projects/material/src/lib/material.module.ts b/projects/material/src/lib/material.module.ts deleted file mode 100644 index a407043..0000000 --- a/projects/material/src/lib/material.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { DropzoneCdkModule } from '@ngx-dropzone/cdk'; -import { MatDropzone } from './mat-dropzone/mat-dropzone.component'; - -@NgModule({ - declarations: [MatDropzone], - imports: [CommonModule, ReactiveFormsModule, MatFormFieldModule, MatChipsModule, MatIconModule, DropzoneCdkModule], - exports: [MatDropzone], -}) -export class DropzoneMaterialModule {} diff --git a/projects/material/src/public-api.ts b/projects/material/src/public-api.ts index b729c4c..46a7ac2 100644 --- a/projects/material/src/public-api.ts +++ b/projects/material/src/public-api.ts @@ -3,4 +3,3 @@ */ export * from './lib/mat-dropzone'; -export * from './lib/material.module'; diff --git a/readme.md b/readme.md index edf4314..e4b746f 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,7 @@ npm install @ngx-dropzone/cdk @ngx-dropzone/material ## Versioning For the versioning, we stay consistent with the major Angular releases. -So Angular (components) 18 will be compatible with `@ngx-dropzone/cdk@18.x.x`. +So Angular (components) 19 will be compatible with `@ngx-dropzone/cdk@19.x.x`. Please note, that v16 is the first officially supported version. For older Angular releases, use the libs at your own risk. @@ -48,22 +48,24 @@ For older Angular releases, use the libs at your own risk. This describes how to use the Material dropzone. If you want to extend the CDK with your own styling, see below. -```js -// in app.module.ts -import { MatFormFieldModule } from '@angular/material/form-field'; -import { DropzoneCdkModule } from '@ngx-dropzone/cdk'; -import { DropzoneMaterialModule } from '@ngx-dropzone/material'; +```ts +// in app.component.ts +import { MatFormField, MatLabel } from '@angular/material/form-field'; +import { MatIcon } from '@angular/material/icon'; +import { FileInputDirective } from '@ngx-dropzone/cdk'; +import { MatDropzone } from '@ngx-dropzone/material'; -@NgModule({ +@Component({ ... imports: [ - MatFormFieldModule, - DropzoneCdkModule, - DropzoneMaterialModule, + MatFormField, + MatLabel, + MatIcon, + MatDropzone, + FileInputDirective, ], ... }) -export class AppModule { } ``` Now you can use it in your markup. @@ -88,27 +90,19 @@ for `[(ngModel)]` and `[formControl]` directives, so you can seamlessly integrat file upload into your form. First, make sure to import the `ReactiveFormsModule`. +Then, you're able to define your form control element (incl. validation). -```js -// in app.module.ts +```ts +// in app.component.ts import { ReactiveFormsModule } from '@angular/forms'; -@NgModule({ - ... +@Component({ + selector: "form-control-dropzone", imports: [ ReactiveFormsModule, + MatError, ... ], - ... -}) -export class AppModule { } -``` - -Then, you're able to define your form control element (incl. validation). - -```ts -@Component({ - selector: "form-control-dropzone", template: ` @@ -164,7 +158,7 @@ because people are way too opinionated about their styling and behaviour. ``` -```js +```ts export class AppComponent { fileCtrl = new FormControl(); @@ -212,32 +206,18 @@ However, you might want to apply your own custom styling (or library). In this case, you're able to build upon the dropzone CDK. See the [Material dropzone](/projects/material/src/lib/mat-dropzone/mat-dropzone.component.ts) as an example. -The basic setup requires you to import the `DropzoneCdkModule` into your app. - -```js -// in app.module.ts -import { DropzoneCdkModule } from '@ngx-dropzone/cdk'; - -@NgModule({ - ... - imports: [ - DropzoneCdkModule, - ], - ... -}) -export class AppModule { } -``` - -Next up, you extend the `DropzoneComponent` and apply your own styling and functionality. +The basic setup requires you to extend the `DropzoneComponent` in your app to apply your own styling and functionality. Use the following skeleton as a starting point. You may always have a look at the Material reference implementation linked above. ```ts import { Component } from "@angular/core"; -import { DropzoneComponent } from "@ngx-dropzone/cdk"; +import { AcceptService, DropzoneComponent, DropzoneService, FileInputDirective } from "@ngx-dropzone/cdk"; @Component({ selector: "my-dropzone", + imports: [FileInputDirective, DropzoneComponent], + providers: [DropzoneService, AcceptService], template: `
diff --git a/tsconfig.json b/tsconfig.json index 63de7dc..9c9255a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,8 @@ "target": "ES2022", "module": "es2020", "lib": ["es2018", "dom"], - "useDefineForClassFields": false + "isolatedModules": true, + "useDefineForClassFields": true }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false,