diff --git a/showcase/package-lock.json b/showcase/package-lock.json index ab910b067b..27af40c2c4 100644 --- a/showcase/package-lock.json +++ b/showcase/package-lock.json @@ -949,15 +949,6 @@ "@babel/types": "^7.13.12" } }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, "@babel/helper-split-export-declaration": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", @@ -1028,6 +1019,17 @@ "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", "@babel/plugin-proposal-optional-chaining": "^7.13.12" + }, + "dependencies": { + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + } } }, "@babel/plugin-proposal-async-generator-functions": { @@ -1071,16 +1073,6 @@ "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", - "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, "@babel/plugin-proposal-json-strings": { "version": "7.13.8", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", @@ -1091,16 +1083,6 @@ "@babel/plugin-syntax-json-strings": "^7.8.3" } }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", - "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, "@babel/plugin-proposal-nullish-coalescing-operator": { "version": "7.13.8", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", @@ -1153,6 +1135,17 @@ "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "dependencies": { + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + } } }, "@babel/plugin-proposal-private-methods": { @@ -1577,6 +1570,17 @@ "requires": { "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + }, + "dependencies": { + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + } } }, "@babel/plugin-transform-sticky-regex": { @@ -1704,6 +1708,28 @@ "babel-plugin-polyfill-regenerator": "^0.2.0", "core-js-compat": "^3.9.0", "semver": "^6.3.0" + }, + "dependencies": { + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", + "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + } } }, "@babel/preset-modules": { @@ -1986,9 +2012,17 @@ "resolved": "https://registry.npmjs.org/@nationalbankbelgium/code-style/-/code-style-1.5.0.tgz", "integrity": "sha512-I2c4/j/jbE0/RkCQ0cbT2b1UF/SqVlENNSCvmIi0afpIURmaFyIu1o6w+nEbS+SugcBtCsidfxllRqGtza0KNA==" }, + "@nationalbankbelgium/ngx-form-errors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nationalbankbelgium/ngx-form-errors/-/ngx-form-errors-1.0.0.tgz", + "integrity": "sha512-14eiHMfRlr6fQk/D6Azb59EDMWR/4qHHPApfKih/U9MmTScRCWNzpN54A8jRbMYQX0MvxCHOhMkk31jM3y095g==", + "requires": { + "tslib": "^1.9.0" + } + }, "@nationalbankbelgium/stark-build": { - "version": "file:../dist/packages-dist/stark-build/nationalbankbelgium-stark-build-10.2.0-4c6c9a6d.tgz", - "integrity": "sha512-LOb6l7FTrWW0Fule3FiyXGYz7c2w8IVfo8gczur+xqxpXLh+8ERpGdONsC0n+dk36Vz/FyEhg0cFfIRDs9T3dw==", + "version": "file:../dist/packages-dist/stark-build/nationalbankbelgium-stark-build-10.2.0-092ad9c6.tgz", + "integrity": "sha512-hWWsGU+ac3LTZhnskOgJ/TRb6fbxCjCq9guAFestFx2X8qH+qkCZVMXpWNk9tumYJLvskiaKt3LU1UZRIUNNqg==", "dev": true, "requires": { "@angular-builders/custom-webpack": "^8.3.0", @@ -2013,8 +2047,8 @@ } }, "@nationalbankbelgium/stark-core": { - "version": "file:../dist/packages-dist/stark-core/nationalbankbelgium-stark-core-10.2.0-4c6c9a6d.tgz", - "integrity": "sha512-uL3//JiGKvNnNWK1vF4CI8LsfOBVELw9Vd9X8IXDvOkuA3Dgj+pEv6n82QezZkLECtT+I5edAJVCaF6imSNptw==", + "version": "file:../dist/packages-dist/stark-core/nationalbankbelgium-stark-core-10.2.0-092ad9c6.tgz", + "integrity": "sha512-sMXaFILMlxCntLdOH5WKgHkq4/djG0nGALV96iCIfcYjJxOlMcETCZSlmRZeaKNNDrPKP6vyPohbVza7FuT51Q==", "requires": { "@angularclass/hmr": "^3.0.0", "@ng-idle/core": "^8.0.0-beta.4", @@ -2046,8 +2080,8 @@ } }, "@nationalbankbelgium/stark-rbac": { - "version": "file:../dist/packages-dist/stark-rbac/nationalbankbelgium-stark-rbac-10.2.0-4c6c9a6d.tgz", - "integrity": "sha512-ZUcWz3v0m+v+YPXBY6RdOlUzcoDv4HxLArgmG+8lbbZgXrad2hx6zvpXKjwpeer/7OZQTj/xfEiZYxnvXJ0xCA==", + "version": "file:../dist/packages-dist/stark-rbac/nationalbankbelgium-stark-rbac-10.2.0-092ad9c6.tgz", + "integrity": "sha512-xewbqrtGWLqaN3kaO+yZjTx3e/+GMctgeU72D9z0JsP76fDOBXIirirtWG8Y/Puk+Bynx0+AjoQl3seg91xF+w==", "requires": { "@types/lodash-es": "^4.17.1", "tslib": "^1.9.0" @@ -2061,8 +2095,8 @@ } }, "@nationalbankbelgium/stark-testing": { - "version": "file:../dist/packages-dist/stark-testing/nationalbankbelgium-stark-testing-10.2.0-4c6c9a6d.tgz", - "integrity": "sha512-IGiEzqGs4Ngx2QR2g108WkH7cD9JNGZi67CN0QSCjo34zXuDYlhJyw47R495huO6VWRsyNANJw++u0RUd+RkeA==", + "version": "file:../dist/packages-dist/stark-testing/nationalbankbelgium-stark-testing-10.2.0-092ad9c6.tgz", + "integrity": "sha512-Ibp+kM8wuHs8Kscy04x+etyYBrzA2TzzQwxa8UwTOEtzGKr5a6NUr2VXS0Z+TGoiIjPlqcx6BQxGAM+qV1nRQw==", "dev": true, "requires": { "@angular-devkit/build-angular": ">= 0.800.0 < 0.900.0", @@ -2087,8 +2121,8 @@ } }, "@nationalbankbelgium/stark-ui": { - "version": "file:../dist/packages-dist/stark-ui/nationalbankbelgium-stark-ui-10.2.0-4c6c9a6d.tgz", - "integrity": "sha512-nHc77NL4kMFbJ5i5hBwf4os4uNsxHdZsoJvyWN41/+GOHWyPHfDto9rgiFhEGbvFuiw8hv+c+5DxWNS4JJ1dEQ==", + "version": "file:../dist/packages-dist/stark-ui/nationalbankbelgium-stark-ui-10.2.0-092ad9c6.tgz", + "integrity": "sha512-uczZlPjnHPsew8fvBSIt5T2NbIzAvdB2/hhaR0F/lNA74TauyuiK+BunpbWbNjPZZqUiKGcaviWrizFvVMLZnQ==", "requires": { "@angular/material-moment-adapter": "^8.2.0", "@mdi/angular-material": "^4.0.96", @@ -15859,8 +15893,7 @@ "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" }, "tslint": { "version": "5.20.1", diff --git a/showcase/package.json b/showcase/package.json index fe73c3baa0..c37f980a36 100644 --- a/showcase/package.json +++ b/showcase/package.json @@ -120,9 +120,10 @@ "@angular/platform-server": "^8.2.0", "@angular/router": "^8.2.0", "@nationalbankbelgium/code-style": "^1.5.0", - "@nationalbankbelgium/stark-core": "file:../dist/packages-dist/stark-core/nationalbankbelgium-stark-core-10.2.0-4c6c9a6d.tgz", - "@nationalbankbelgium/stark-rbac": "file:../dist/packages-dist/stark-rbac/nationalbankbelgium-stark-rbac-10.2.0-4c6c9a6d.tgz", - "@nationalbankbelgium/stark-ui": "file:../dist/packages-dist/stark-ui/nationalbankbelgium-stark-ui-10.2.0-4c6c9a6d.tgz", + "@nationalbankbelgium/ngx-form-errors": "^1.0.0", + "@nationalbankbelgium/stark-core": "file:../dist/packages-dist/stark-core/nationalbankbelgium-stark-core-10.2.0-092ad9c6.tgz", + "@nationalbankbelgium/stark-rbac": "file:../dist/packages-dist/stark-rbac/nationalbankbelgium-stark-rbac-10.2.0-092ad9c6.tgz", + "@nationalbankbelgium/stark-ui": "file:../dist/packages-dist/stark-ui/nationalbankbelgium-stark-ui-10.2.0-092ad9c6.tgz", "@uirouter/visualizer": "~7.2.1", "angular-in-memory-web-api": "~0.9.0", "basscss": "~8.1.0", @@ -138,8 +139,8 @@ }, "devDependencies": { "@compodoc/compodoc": "~1.1.11", - "@nationalbankbelgium/stark-build": "file:../dist/packages-dist/stark-build/nationalbankbelgium-stark-build-10.2.0-4c6c9a6d.tgz", - "@nationalbankbelgium/stark-testing": "file:../dist/packages-dist/stark-testing/nationalbankbelgium-stark-testing-10.2.0-4c6c9a6d.tgz", + "@nationalbankbelgium/stark-build": "file:../dist/packages-dist/stark-build/nationalbankbelgium-stark-build-10.2.0-092ad9c6.tgz", + "@nationalbankbelgium/stark-testing": "file:../dist/packages-dist/stark-testing/nationalbankbelgium-stark-testing-10.2.0-092ad9c6.tgz", "@types/core-js": "~2.5.4", "@types/hammerjs": "~2.0.39", "@types/node": "~10.17.56", diff --git a/showcase/src/app/app-menu.config.ts b/showcase/src/app/app-menu.config.ts index d827e2ec4c..0506d70ee6 100644 --- a/showcase/src/app/app-menu.config.ts +++ b/showcase/src/app/app-menu.config.ts @@ -26,6 +26,13 @@ export const APP_MENU_CONFIG: StarkMenuConfig = { isVisible: true, isEnabled: true, targetState: "news" + }, + { + id: "reactive-form-errors", + label: "SHOWCASE.NGX_FORM_ERRORS.TITLE", + isVisible: true, + isEnabled: true, + targetState: "reactive-form-errors" } ] }, diff --git a/showcase/src/app/welcome/pages/index.ts b/showcase/src/app/welcome/pages/index.ts index 92141ec039..5d600592cf 100644 --- a/showcase/src/app/welcome/pages/index.ts +++ b/showcase/src/app/welcome/pages/index.ts @@ -2,3 +2,4 @@ export * from "./getting-started"; export * from "./home"; export * from "./news"; export * from "./no-content"; +export * from "./reactive-form-errors"; diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.html b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.html new file mode 100644 index 0000000000..42620e9a7a --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.html @@ -0,0 +1,3 @@ + + + diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.scss b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.scss new file mode 100644 index 0000000000..e61ec8f140 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.scss @@ -0,0 +1,5 @@ +:host mat-card { + box-sizing: border-box; + width: 100%; + min-height: 100%; +} diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.ts b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.ts new file mode 100644 index 0000000000..23dbfa5e32 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.component.ts @@ -0,0 +1,46 @@ +import { Component, HostBinding, Input } from "@angular/core"; + +type Colors = "primary" | "accent" | "warning" | "success"; + +@Component({ + selector: "app-card", + templateUrl: "./card.component.html", + styleUrls: ["./card.component.scss"] +}) +export class CardComponent { + @HostBinding("class.app-card") + public cssClass = true; + @HostBinding("class.app-color-primary") + public primaryColor!: boolean; + @HostBinding("class.app-color-accent") + public accentColor!: boolean; + @HostBinding("class.app-color-warning") + public warningColor!: boolean; + @HostBinding("class.app-color-success") + public successColor!: boolean; + + @Input() + public set color(color: Colors) { + this.primaryColor = false; + this.accentColor = false; + this.warningColor = false; + this.successColor = false; + + switch (color) { + case "primary": + this.primaryColor = true; + break; + case "accent": + this.accentColor = true; + break; + case "warning": + this.warningColor = true; + break; + case "success": + this.successColor = true; + break; + default: + break; + } + } +} diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.theme.scss b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.theme.scss new file mode 100644 index 0000000000..d4e3871de0 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/card.theme.scss @@ -0,0 +1,20 @@ +.app-card.app-color-primary mat-card { + background-color: mat-color($primary-palette, 500); + color: mat-contrast($primary-palette, 500); +} + +.app-card.app-color-accent mat-card { + background-color: mat-color($primary-palette, 500); + color: mat-contrast($primary-palette, 500); +} + +.app-card.app-color-warning mat-card { + background-color: mat-color($warning-palette, 500); + color: mat-contrast($warning-palette, 500); +} + +.app-card.app-color-success mat-card { + /*Themes do not have a success map by default*/ + background-color: mat-color($success-palette, 500); + color: mat-contrast($success-palette, 500); +} diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/card/index.ts b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/index.ts new file mode 100644 index 0000000000..8151bac4c8 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/card/index.ts @@ -0,0 +1 @@ +export * from "./card.component"; diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/index.ts b/showcase/src/app/welcome/pages/reactive-form-errors/components/index.ts new file mode 100644 index 0000000000..a81d5c6e8a --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/index.ts @@ -0,0 +1,2 @@ +export * from "./card"; +export * from "./translated-form-error"; diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/index.ts b/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/index.ts new file mode 100644 index 0000000000..4fad8db434 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/index.ts @@ -0,0 +1 @@ +export * from "./translated-form-error.component"; diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/translated-form-error.component.html b/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/translated-form-error.component.html new file mode 100644 index 0000000000..22e7aaf61f --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/translated-form-error.component.html @@ -0,0 +1 @@ +
{{ error.message | translate: error.params }}
diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/translated-form-error.component.ts b/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/translated-form-error.component.ts new file mode 100644 index 0000000000..c441df4199 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/components/translated-form-error/translated-form-error.component.ts @@ -0,0 +1,47 @@ +import { Component, HostBinding, OnInit } from "@angular/core"; +import { LangChangeEvent, TranslateService } from "@ngx-translate/core"; +import { Observable } from "rxjs"; +import { NgxFormErrorComponent, NgxFormFieldError } from "@nationalbankbelgium/ngx-form-errors"; + +@Component({ + selector: "app-translated-form-error", + templateUrl: "./translated-form-error.component.html" +}) +export class TranslatedFormErrorComponent implements NgxFormErrorComponent, OnInit { + @HostBinding("class") + public cssClass = "translated-form-error"; + + public errors: NgxFormFieldError[] = []; + public errors$!: Observable; + public fieldName!: string; + + public constructor(public translateService: TranslateService) {} + + public ngOnInit(): void { + this.translateService.onLangChange.subscribe((_ev: LangChangeEvent) => { + this.updateTranslateFieldName(this.translateService.instant(this.fieldName)); + }); + } + + public subscribeToErrors(): void { + this.errors$.subscribe((errors: NgxFormFieldError[]) => { + this.errors = errors; + + if (errors.length) { + // the formField can be retrieved from the "fieldName" param of any of the errors + this.fieldName = errors[0].params.fieldName; + this.updateTranslateFieldName(this.translateService.instant(this.fieldName)); + } + }); + } + + public updateTranslateFieldName(translatedFieldName: string): void { + for (const error of this.errors) { + error.params = { ...error.params, fieldName: translatedFieldName }; + } + } + + public trackError(index: number): number { + return index; + } +} diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/index.ts b/showcase/src/app/welcome/pages/reactive-form-errors/index.ts new file mode 100644 index 0000000000..e2c99e3f05 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/index.ts @@ -0,0 +1 @@ +export * from "./reactive-form-errors-page.component"; diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/password-validator.ts b/showcase/src/app/welcome/pages/reactive-form-errors/password-validator.ts new file mode 100644 index 0000000000..a87ca4f312 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/password-validator.ts @@ -0,0 +1,31 @@ +import { FormControl, FormGroup, ValidationErrors, ValidatorFn } from "@angular/forms"; + +export class PasswordValidator { + // Inspired on: http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview + public static areEqual(formGroup: FormGroup): ValidationErrors | null { + let value: string | undefined; + let valid = true; + + for (const key in formGroup.controls) { + if (formGroup.controls.hasOwnProperty(key)) { + const control: FormControl = formGroup.controls[key]; + + if (value === undefined) { + value = control.value; + } else if (value !== control.value) { + valid = false; + break; + } + } + } + + /* tslint:disable-next-line:no-null-keyword */ + return valid ? null : { areEqual: true }; + } +} + +export function getConfirmPasswordValidator(formGroup: FormGroup): ValidatorFn { + return (): ValidationErrors | null => { + return PasswordValidator.areEqual(formGroup); + }; +} diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.html b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.html new file mode 100644 index 0000000000..dfc04fc54b --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.html @@ -0,0 +1,230 @@ +
+

+ SHOWCASE.NGX_FORM_ERRORS.TITLE +

+

SHOWCASE.DEMO.SHARED.EXAMPLE_VIEWER_LIST

+
+ +
+
+ + + + + + + + + +
+ + + + + + + + + + + + + +
+
+ + + + + + +
+ +
+ + SHOWCASE.NGX_FORM_ERRORS.FIELDS.USER_NAME + +
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.HAS_ERRORS" + | translate: { hasErrors: usernameField.hasErrors } + }} +
+
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.HAS_SPECIFIC_ERROR" + | translate: { error: "required", hasError: usernameField.hasError("required") } + }} +
+
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.IS_TOUCHED" + | translate: { isTouched: usernameField.hasState("touched") } + }} +
+
+ {{ "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.ERROR" | translate: { error: "required" } }} +
{{ usernameField.getError("required") | json }}
+
+
+ {{ "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.ERRORS" | translate }} +
{{ usernameField.errors | json }}
+
+
+
+ + + SHOWCASE.NGX_FORM_ERRORS.FIELDS.PASSWORD + +
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.HAS_ERRORS" + | translate: { hasErrors: passwordField.hasErrors } + }} +
+
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.HAS_SPECIFIC_ERROR" + | translate: { error: "pattern", hasError: passwordField.hasError("pattern") } + }} +
+
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.IS_TOUCHED" + | translate: { isTouched: passwordField.hasState("touched") } + }} +
+
+ {{ "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.ERROR" | translate: { error: "pattern" } }} +
{{ passwordField.getError("pattern") | json }}
+
+
+ {{ "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.ERRORS" | translate }} +
{{ passwordField.errors | json }}
+
+
+
+ + + SHOWCASE.NGX_FORM_ERRORS.FIELDS.CONFIRM_PASSWORD + +
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.HAS_ERRORS" + | translate: { hasErrors: confirmPasswordField.hasErrors } + }} +
+
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.HAS_SPECIFIC_ERROR" + | translate: { error: "required", hasError: confirmPasswordField.hasError("required") } + }} +
+
+ {{ + "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.IS_TOUCHED" + | translate: { isTouched: confirmPasswordField.hasState("touched") } + }} +
+
+ {{ "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.ERROR" | translate: { error: "required" } }} +
{{ confirmPasswordField.getError("required") | json }}
+
+
+ {{ "SHOWCASE.NGX_FORM_ERRORS.FIELDS.INFO.ERRORS" | translate }} +
{{ confirmPasswordField.errors | json }}
+
+
+
+
+ + + + No validation errors + + +
+ + + + + + + + + + + +
+
+
+
+
+
+ +
diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.scss b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.scss new file mode 100644 index 0000000000..f2c59487b0 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.scss @@ -0,0 +1,68 @@ +@import "~@nationalbankbelgium/stark-ui/assets/styles/media-queries"; + +button { + margin: 8px; +} + +.form-card { + mat-form-field { + box-sizing: border-box; + width: 100%; + @media #{$desktop-screen-query} { + width: 45%; + &:last-child { + margin-left: 10%; + } + } + } + + mat-card-actions { + display: flex; + align-items: flex-start; + flex-wrap: wrap; + margin: -10px; + + button { + margin: 10px; + @media #{$mobile-only-query} { + width: 100%; + } + } + } +} + +.form-field-info { + @media #{$desktop-query} { + max-width: 33%; + } + + max-width: 100%; + + mat-card-content { + margin: 0; + padding: 5px 0; + + pre { + overflow: auto; + box-sizing: border-box; + display: block; + max-height: 200px; + + margin: inherit; + padding: 15px 5px; + border-radius: 4px; + + background-color: rgba(0, 0, 0, 0.2); + + word-break: break-all; + white-space: pre-wrap; + + /* Non standard for webkit */ + + hyphens: auto; + &:empty { + display: none; + } + } + } +} diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.theme.scss b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.theme.scss new file mode 100644 index 0000000000..f73a5db4a1 --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.theme.scss @@ -0,0 +1,19 @@ +.app-card mat-form-field.maximum-height { + .mat-form-field-wrapper { + padding-bottom: 40px; + + .mat-form-field-underline { + bottom: 40px; + } + + .mat-form-field-subscript-wrapper { + top: calc(100% - 40px); + } + } +} + +.validation-summary { + .translated-form-error div::before { + content: "• "; + } +} diff --git a/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.ts b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.ts new file mode 100644 index 0000000000..89b1b64c6b --- /dev/null +++ b/showcase/src/app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.ts @@ -0,0 +1,74 @@ +import { Component, Inject } from "@angular/core"; +import { AbstractControl, FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { getConfirmPasswordValidator } from "./password-validator"; +import { ReferenceLink } from "../../../shared/components/reference-block"; + +@Component({ + selector: "reactive-forms", + templateUrl: "./reactive-form-errors-page.component.html", + styleUrls: ["./reactive-form-errors-page.component.scss"] +}) +export class ReactiveFormErrorsPageComponent { + public collapsed: boolean[] = [false, false, true]; + + public referenceList: ReferenceLink[] = [ + { + label: "NGX Form errors library", + url: "https://github.com/NationalBankBelgium/ngx-form-errors" + } + ]; + + public formGroup: FormGroup; + public passwordPattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$"; + public showValidationDetails = false; + public showValidationSummary = true; + + public constructor(private formBuilder: FormBuilder, @Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) { + this.formGroup = this.formBuilder.group({ + username: [undefined, Validators.required], + matchingPasswords: this.formBuilder.group({ + password: [ + "", + Validators.compose([ + Validators.minLength(3), + Validators.maxLength(10), + Validators.required, + // this is for the letters (both uppercase and lowercase) and numbers validation + Validators.pattern(this.passwordPattern) + ]) + ], + confirmPassword: [""] // validators for this field to be set afterwards (see below) + }) + }); + + // setting the validator for confirmPassword field once we have created the form group + const confirmPasswordControl = this.formGroup.get("matchingPasswords.confirmPassword"); + // we need to set the confirmPasswordValidator passing the "matchingPasswords" form group so that the errors of the form group are actually + // linked to the "confirmPassword" control because the NgxFormErrors directive is linked to the control and not to the form group! + confirmPasswordControl.setValidators([ + Validators.required, + getConfirmPasswordValidator(this.formGroup.get("matchingPasswords")) + ]); + } + + public getErrorClass(formControlName: string): string { + const formCtrl = this.formGroup.get(formControlName) as AbstractControl; + return formCtrl.errors && Object.keys(formCtrl.errors).length > 1 ? "maximum-height" : "small-height"; + } + + public toggleCollapsible(nb: number): void { + this.collapsed[nb] = !this.collapsed[nb]; + } + public toggleValidationDetails(): void { + this.showValidationDetails = !this.showValidationDetails; + } + + public toggleValidationSummary(): void { + this.showValidationSummary = !this.showValidationSummary; + } + + public onSubmitUserDetails(formGroup: FormGroup): void { + this.logger.info("Submitted form:", formGroup.value); + } +} diff --git a/showcase/src/app/welcome/routes.ts b/showcase/src/app/welcome/routes.ts index 6ea7383b2a..a27c90de0b 100644 --- a/showcase/src/app/welcome/routes.ts +++ b/showcase/src/app/welcome/routes.ts @@ -1,5 +1,11 @@ import { Ng2StateDeclaration } from "@uirouter/angular"; -import { GettingStartedPageComponent, HomePageComponent, NewsPageComponent, NoContentPageComponent } from "./pages"; +import { + GettingStartedPageComponent, + HomePageComponent, + NewsPageComponent, + NoContentPageComponent, + ReactiveFormErrorsPageComponent +} from "./pages"; export const NEWS_STATES: Ng2StateDeclaration[] = [ { @@ -29,6 +35,15 @@ export const NEWS_STATES: Ng2StateDeclaration[] = [ views: { "@": { component: NewsPageComponent } }, parent: "app" }, + { + name: "reactive-form-errors", + url: "^/reactive-form-errors", // use ^ to avoid double slash "//" in the URL after the domain (https://github.com/angular-ui/ui-router/wiki/URL-Routing#absolute-routes-) + data: { + translationKey: "SHOWCASE.NGX_FORM_ERRORS.TITLE" + }, + views: { "@": { component: ReactiveFormErrorsPageComponent } }, + parent: "app" + }, { name: "otherwise", url: "^/otherwise", // use ^ to avoid double slash "//" in the URL after the domain (https://github.com/angular-ui/ui-router/wiki/URL-Routing#absolute-routes-) diff --git a/showcase/src/app/welcome/welcome.module.ts b/showcase/src/app/welcome/welcome.module.ts index b7774496ec..b888bcc88e 100644 --- a/showcase/src/app/welcome/welcome.module.ts +++ b/showcase/src/app/welcome/welcome.module.ts @@ -1,8 +1,19 @@ import { NgModule } from "@angular/core"; import { UIRouterModule } from "@uirouter/angular"; -import { GettingStartedPageComponent, HomePageComponent, NewsPageComponent, NoContentPageComponent } from "./pages"; -import { NewsItemComponent } from "./components"; +import { MatDividerModule } from "@angular/material/divider"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatInputModule } from "@angular/material/input"; +import { NgxFormErrorsModule, NgxFormErrorsMessageService } from "@nationalbankbelgium/ngx-form-errors"; import { SharedModule } from "../shared"; +import { + GettingStartedPageComponent, + HomePageComponent, + NewsPageComponent, + NoContentPageComponent, + ReactiveFormErrorsPageComponent +} from "./pages"; +import {CardComponent, TranslatedFormErrorComponent} from "./pages/reactive-form-errors/components"; +import { NewsItemComponent } from "./components"; import { NEWS_STATES } from "./routes"; @NgModule({ @@ -10,9 +21,48 @@ import { NEWS_STATES } from "./routes"; UIRouterModule.forChild({ states: NEWS_STATES }), - SharedModule + SharedModule, + MatDividerModule, + MatInputModule, + MatFormFieldModule, + NgxFormErrorsModule.forRoot({ formErrorComponent: TranslatedFormErrorComponent }) + ], + declarations: [ + GettingStartedPageComponent, + HomePageComponent, + NoContentPageComponent, + NewsPageComponent, + NewsItemComponent, + ReactiveFormErrorsPageComponent, + TranslatedFormErrorComponent, + CardComponent ], - declarations: [GettingStartedPageComponent, HomePageComponent, NoContentPageComponent, NewsPageComponent, NewsItemComponent], - exports: [GettingStartedPageComponent, HomePageComponent, NoContentPageComponent, NewsPageComponent, NewsItemComponent] + exports: [ + GettingStartedPageComponent, + HomePageComponent, + NoContentPageComponent, + NewsPageComponent, + NewsItemComponent, + ReactiveFormErrorsPageComponent + ], + entryComponents: [TranslatedFormErrorComponent] }) -export class WelcomeModule {} +export class WelcomeModule { + /* tslint:disable:no-hardcoded-credentials */ + public constructor(private errorMessageService: NgxFormErrorsMessageService) { + this.errorMessageService.addErrorMessages({ + required: "SHOWCASE.NGX_FORM_ERRORS.FORM.VALIDATION.REQUIRED", + "matchingPasswords.password.required": "SHOWCASE.NGX_FORM_ERRORS.FORM.VALIDATION.PASSWORD_REQUIRED", + minlength: "SHOWCASE.NGX_FORM_ERRORS.FORM.VALIDATION.PASSWORD.MIN_LENGTH", + maxlength: "SHOWCASE.NGX_FORM_ERRORS.FORM.VALIDATION.PASSWORD.MAX_LENGTH", + pattern: "SHOWCASE.NGX_FORM_ERRORS.FORM.VALIDATION.PASSWORD.PATTERN", + areEqual: "SHOWCASE.NGX_FORM_ERRORS.FORM.VALIDATION.CONFIRM_PASSWORD.ARE_EQUAL" + }); + + this.errorMessageService.addFieldNames({ + username: "SHOWCASE.NGX_FORM_ERRORS.FIELDS.ALIAS.USER_NAME", + "matchingPasswords.password": "not used, the alias defined via the directive takes precedence over this", + "matchingPasswords.confirmPassword": "SHOWCASE.NGX_FORM_ERRORS.FIELDS.ALIAS.CONFIRM_PASSWORD" + }); + } +} diff --git a/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.html b/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.html new file mode 100644 index 0000000000..29ac3de932 --- /dev/null +++ b/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.html @@ -0,0 +1,139 @@ +
+
+ + + + + + + + + +
+ + + + + + + + + + + + + +
+
+ + + + + + +
+ +
+ + User Name + +
Has errors: {{ usernameField.hasErrors }}
+
Has 'required' error: {{ usernameField.hasError("required") }}
+
Is touched: {{ usernameField.hasState("touched") }}
+
+ 'required' error: +
{{ usernameField.getError("required") | json }}
+
+
+ Errors: +
{{ usernameField.errors | json }}
+
+
+
+ + + Password + +
Has errors: {{ passwordField.hasErrors }}
+
Has 'pattern' error: {{ passwordField.hasError("pattern") }}
+
Is touched: {{ passwordField.hasState("touched") }}
+
+ 'pattern' error: +
{{ passwordField.getError("pattern") | json }}
+
+
+ Errors: +
{{ passwordField.errors | json }}
+
+
+
+ + + Confirm password + +
Has errors: {{ confirmPasswordField.hasErrors }}
+
Has 'required' error: {{ confirmPasswordField.hasError("required") }}
+
Is touched: {{ confirmPasswordField.hasState("touched") }}
+
+ 'required' error: +
{{ confirmPasswordField.getError("required") | json }}
+
+
+ Errors: +
{{ confirmPasswordField.errors | json }}
+
+
+
+
+ + + + No validation errors + + +
+ + + + + + + + + + + +
+
+
+
diff --git a/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.scss b/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.scss new file mode 100644 index 0000000000..f2c59487b0 --- /dev/null +++ b/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.scss @@ -0,0 +1,68 @@ +@import "~@nationalbankbelgium/stark-ui/assets/styles/media-queries"; + +button { + margin: 8px; +} + +.form-card { + mat-form-field { + box-sizing: border-box; + width: 100%; + @media #{$desktop-screen-query} { + width: 45%; + &:last-child { + margin-left: 10%; + } + } + } + + mat-card-actions { + display: flex; + align-items: flex-start; + flex-wrap: wrap; + margin: -10px; + + button { + margin: 10px; + @media #{$mobile-only-query} { + width: 100%; + } + } + } +} + +.form-field-info { + @media #{$desktop-query} { + max-width: 33%; + } + + max-width: 100%; + + mat-card-content { + margin: 0; + padding: 5px 0; + + pre { + overflow: auto; + box-sizing: border-box; + display: block; + max-height: 200px; + + margin: inherit; + padding: 15px 5px; + border-radius: 4px; + + background-color: rgba(0, 0, 0, 0.2); + + word-break: break-all; + white-space: pre-wrap; + + /* Non standard for webkit */ + + hyphens: auto; + &:empty { + display: none; + } + } + } +} diff --git a/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.ts b/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.ts new file mode 100644 index 0000000000..a89fabed04 --- /dev/null +++ b/showcase/src/assets/examples/reactive-form-errors/reactive-form-errors.ts @@ -0,0 +1,66 @@ +import { Component, Inject } from "@angular/core"; +import { AbstractControl, FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { getConfirmPasswordValidator } from "./password-validator"; + +@Component({ + selector: "reactive-form-errors", + templateUrl: "./reactive-form-errors.html", + styleUrls: ["./reactive-form-errors.scss"] +}) +export class ReactiveFormErrors { + public collapsed: boolean[] = [false, false, true]; + + public formGroup: FormGroup; + public passwordPattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$"; + public showValidationDetails = false; + public showValidationSummary = true; + + public constructor(private formBuilder: FormBuilder, @Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) { + this.formGroup = this.formBuilder.group({ + username: [undefined, Validators.required], + matchingPasswords: this.formBuilder.group({ + password: [ + "", + Validators.compose([ + Validators.minLength(3), + Validators.maxLength(10), + Validators.required, + // this is for the letters (both uppercase and lowercase) and numbers validation + Validators.pattern(this.passwordPattern) + ]) + ], + confirmPassword: [""] // validators for this field to be set afterwards (see below) + }) + }); + + // setting the validator for confirmPassword field once we have created the form group + const confirmPasswordControl = this.formGroup.get("matchingPasswords.confirmPassword"); + // we need to set the confirmPasswordValidator passing the "matchingPasswords" form group so that the errors of the form group are actually + // linked to the "confirmPassword" control because the NgxFormErrors directive is linked to the control and not to the form group! + confirmPasswordControl.setValidators([ + Validators.required, + getConfirmPasswordValidator(this.formGroup.get("matchingPasswords")) + ]); + } + + public getErrorClass(formControlName: string): string { + const formCtrl = this.formGroup.get(formControlName) as AbstractControl; + return formCtrl.errors && Object.keys(formCtrl.errors).length > 1 ? "maximum-height" : "small-height"; + } + + public toggleCollapsible(nb: number): void { + this.collapsed[nb] = !this.collapsed[nb]; + } + public toggleValidationDetails(): void { + this.showValidationDetails = !this.showValidationDetails; + } + + public toggleValidationSummary(): void { + this.showValidationSummary = !this.showValidationSummary; + } + + public onSubmitUserDetails(formGroup: FormGroup): void { + this.logger.info("Submitted form:", formGroup.value); + } +} diff --git a/showcase/src/assets/translations/en.json b/showcase/src/assets/translations/en.json index f79504ad46..3d56f46f52 100644 --- a/showcase/src/assets/translations/en.json +++ b/showcase/src/assets/translations/en.json @@ -432,6 +432,49 @@ "NEWS": { "TITLE": "News" }, + "NGX_FORM_ERRORS": { + "TITLE": "Reactive Forms errors", + "EXAMPLE": "Example", + "FORM": { + "VALIDATION": { + "REQUIRED": "{{fieldName}} is required", + "PASSWORD_REQUIRED": "{{fieldName}} must be provided", + "USER_NAME": { + "UNIQUE": "This username has already been taken" + }, + "PASSWORD": { + "MAX_LENGTH": "Password cannot be more than {{requiredLength}} characters long", + "MIN_LENGTH": "Password must be at least {{requiredLength}} characters long", + "PATTERN": "Password must contain at least one uppercase, one lowercase and one number" + }, + "CONFIRM_PASSWORD": { + "ARE_EQUAL": "Password mismatch" + } + }, + "HIDE_DETAILS": "Hide validation details", + "SHOW_DETAILS": "Show validation details", + "HIDE_SUMMARY": "Hide validation summary", + "SHOW_SUMMARY": "Show validation summary", + "SUBMIT": "Submit" + }, + "FIELDS": { + "USER_NAME": "User Name", + "PASSWORD": "Password", + "CONFIRM_PASSWORD": "Confirm password", + "ALIAS": { + "USER_NAME": "Your username", + "PASSWORD_ALIAS": "A valid password", + "CONFIRM_PASSWORD": "Password confirmation" + }, + "INFO": { + "HAS_ERRORS": "Has errors: {{hasErrors}}", + "HAS_SPECIFIC_ERROR": "Has '{{error}}' error: {{hasError}}", + "ERROR": "'{{error}}' error:", + "ERRORS": "Errors:", + "IS_TOUCHED": "Is touched: {{isTouched}}" + } + } + }, "OTHERWISE": { "TITLE": "Otherwise" }, diff --git a/showcase/src/assets/translations/fr.json b/showcase/src/assets/translations/fr.json index 4e384d6688..0641ba36f6 100644 --- a/showcase/src/assets/translations/fr.json +++ b/showcase/src/assets/translations/fr.json @@ -432,6 +432,49 @@ "NEWS": { "TITLE": "Nouvelles" }, + "NGX_FORM_ERRORS": { + "TITLE": "Reactive Forms errors", + "EXAMPLE": "Exemple", + "FORM": { + "VALIDATION": { + "REQUIRED": "Le champ \"{{fieldName}}\" est requis", + "PASSWORD_REQUIRED": "Le champ \"{{fieldName}}\" est requis", + "USER_NAME": { + "UNIQUE": "Ce nom d'utilisateur est déjà pris" + }, + "PASSWORD": { + "MAX_LENGTH": "Le mot de passe ne peut pas contenir plus de {{requiredLength}} caractères", + "MIN_LENGTH": "Le mot de passe doit contenir au moins {{requiredLength}} caractères", + "PATTERN": "Le mot de passe doit contenir au moins une majuscule, une minuscule et un chiffre." + }, + "CONFIRM_PASSWORD": { + "ARE_EQUAL": "Les mots de passe ne correspondent pas" + } + }, + "HIDE_DETAILS": "Masquer les détails de validation", + "SHOW_DETAILS": "Afficher les détails de validation", + "HIDE_SUMMARY": "Masquer le résumé de validation", + "SHOW_SUMMARY": "Afficher le résumé de validation", + "SUBMIT": "Soumettre" + }, + "FIELDS": { + "USER_NAME": "Nom d'utilisateur", + "PASSWORD": "Mot de passe", + "CONFIRM_PASSWORD": "Confirmez le mot de passe", + "ALIAS": { + "USER_NAME": "Votre nom d'utilisateur", + "PASSWORD_ALIAS": "Un mot de passe valide", + "CONFIRM_PASSWORD": "Confirmation du mot de passe" + }, + "INFO": { + "HAS_ERRORS": "Contient des erreurs: {{hasErrors}}", + "HAS_SPECIFIC_ERROR": "Contient l'erreur '{{error}}': {{hasError}}", + "ERROR": "Erreur '{{error}}':", + "ERRORS": "Erreurs:", + "IS_TOUCHED": "Est touché: {{isTouched}}" + } + } + }, "OTHERWISE": { "TITLE": "Autre" }, diff --git a/showcase/src/assets/translations/nl.json b/showcase/src/assets/translations/nl.json index 5bcee9df46..739bf9688b 100644 --- a/showcase/src/assets/translations/nl.json +++ b/showcase/src/assets/translations/nl.json @@ -432,6 +432,49 @@ "NEWS": { "TITLE": "Nieuws" }, + "NGX_FORM_ERRORS": { + "TITLE": "Reactive Forms errors", + "EXAMPLE": "Voorbeeld", + "FORM": { + "VALIDATION": { + "REQUIRED": "{{fieldName}} is verplicht", + "PASSWORD_REQUIRED": "{{fieldName}} moet worden gegeven", + "USER_NAME": { + "UNIQUE": "Deze gebruikersnaam is al in gebruik" + }, + "PASSWORD": { + "MAX_LENGTH": "Wachtwoord mag niet meer dan {{requiredLength}} tekens lang zijn", + "MIN_LENGTH": "Wachtwoord moet minimaal {{requiredLength}} tekens lang zijn", + "PATTERN": "Wwachtwoord moet minimaal één hoofdletter, één kleine letter en één cijfer bevatten" + }, + "CONFIRM_PASSWORD": { + "ARE_EQUAL": "Wachtwoord komt niet overeen" + } + }, + "HIDE_DETAILS": "Validatiedetails verbergen", + "SHOW_DETAILS": "Validatiedetails weergeven", + "HIDE_SUMMARY": "Validatieoverzicht verbergen", + "SHOW_SUMMARY": "Validatieoverzicht tonen", + "SUBMIT": "Indienen" + }, + "FIELDS": { + "USER_NAME": "Gebruikersnaam", + "PASSWORD": "Wachtwoord", + "CONFIRM_PASSWORD": "Bevestig wachtwoord", + "ALIAS": { + "USER_NAME": "Uw gebruikersnaam", + "PASSWORD_ALIAS": "Een geldig wachtwoord", + "CONFIRM_PASSWORD": "Wachtwoordbevestiging" + }, + "INFO": { + "HAS_ERRORS": "Veld heeft fouten: {{hasErrors}}", + "HAS_SPECIFIC_ERROR": "Veld heeft de '{{error}}' fout: {{hasError}}", + "ERROR": "'{{error}}' fout:", + "ERRORS": "Fouten:", + "IS_TOUCHED": "Veld is aangeraakt: {{isTouched}}" + } + } + }, "OTHERWISE": { "TITLE": "Andersom" }, diff --git a/showcase/src/styles/_theme.scss b/showcase/src/styles/_theme.scss index 34ac649730..a700eff430 100644 --- a/showcase/src/styles/_theme.scss +++ b/showcase/src/styles/_theme.scss @@ -16,3 +16,5 @@ Import the local variables file first to set the correct variables, see: @import "../app/demo-ui/pages/route-search/demo-route-search-page.component-theme"; @import "../app/demo-ui/components/table-regular/table-regular-theme"; @import "../app/styleguide/pages/layout/styleguide-layout-page.theme"; +@import "../app/welcome/pages/reactive-form-errors/components/card/card.theme"; +@import "../app/welcome/pages/reactive-form-errors/reactive-form-errors-page.component.theme";