├── .nvmrc
├── .husky
├── commit-msg
└── pre-commit
├── .npmrc
├── demo-app
├── ng15
│ ├── .npmrc
│ ├── src
│ │ ├── app
│ │ │ ├── components
│ │ │ │ ├── card
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── card.component.html
│ │ │ │ │ ├── card.component.scss
│ │ │ │ │ ├── card.theme.scss
│ │ │ │ │ └── card.component.ts
│ │ │ │ ├── navigation
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── navigation.component.scss
│ │ │ │ │ ├── navigation.component.ts
│ │ │ │ │ ├── navigation.component.html
│ │ │ │ │ └── navigation.theme.scss
│ │ │ │ ├── language-selector
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── language-selector.component.scss
│ │ │ │ │ ├── language-selector.component.html
│ │ │ │ │ ├── language-selector.theme.scss
│ │ │ │ │ └── language-selector.component.ts
│ │ │ │ ├── simple-form-error
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── simple-form-error.component.html
│ │ │ │ │ └── simple-form-error.component.ts
│ │ │ │ ├── translated-form-error
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── translated-form-error.component.html
│ │ │ │ │ └── translated-form-error.component.ts
│ │ │ │ └── index.ts
│ │ │ ├── pages
│ │ │ │ ├── ngx-forms-example
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── ngx-forms-example.component.scss
│ │ │ │ │ └── ngx-forms-example.component.ts
│ │ │ │ ├── reactive-forms-example
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── reactive-forms-example.component.scss
│ │ │ │ │ └── reactive-forms-example.component.ts
│ │ │ │ ├── template-driven-forms-example
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── template-driven-forms-example.component.scss
│ │ │ │ │ └── template-driven-forms-example.component.ts
│ │ │ │ ├── typed-reactive-forms-example
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── matching-password.ts
│ │ │ │ │ ├── typed-reactive-forms-example.component.scss
│ │ │ │ │ └── typed-reactive-forms-example.component.ts
│ │ │ │ └── index.ts
│ │ │ ├── app.component.scss
│ │ │ ├── parent-error-state-matcher.ts
│ │ │ ├── translation.config.ts
│ │ │ ├── app-routing.module.ts
│ │ │ ├── app.component.html
│ │ │ ├── app.component.spec.ts
│ │ │ ├── password-validator.ts
│ │ │ ├── app.component.ts
│ │ │ └── app.module.ts
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── styles
│ │ │ ├── _variables.scss
│ │ │ ├── styles.scss
│ │ │ └── _app.theme.scss
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── test.ts
│ │ ├── assets
│ │ │ ├── img
│ │ │ │ └── github-icon.svg
│ │ │ └── translations
│ │ │ │ ├── en.json
│ │ │ │ ├── nl.json
│ │ │ │ └── fr.json
│ │ └── polyfills.ts
│ ├── e2e
│ │ ├── tsconfig.json
│ │ ├── src
│ │ │ ├── app.po.ts
│ │ │ └── app.e2e-spec.ts
│ │ └── protractor.conf.js
│ ├── tsconfig.spec.json
│ ├── .editorconfig
│ ├── tsconfig.app.json
│ ├── .browserslistrc
│ ├── .eslintrc.json
│ ├── tsconfig.json
│ ├── .gitignore
│ ├── karma.conf.js
│ ├── README.md
│ ├── package.json
│ └── angular.json
└── ng16
│ ├── .npmrc
│ ├── src
│ ├── app
│ │ ├── components
│ │ │ ├── card
│ │ │ │ ├── index.ts
│ │ │ │ ├── card.component.html
│ │ │ │ ├── card.component.scss
│ │ │ │ ├── card.theme.scss
│ │ │ │ └── card.component.ts
│ │ │ ├── navigation
│ │ │ │ ├── index.ts
│ │ │ │ ├── navigation.component.scss
│ │ │ │ ├── navigation.component.ts
│ │ │ │ ├── navigation.component.html
│ │ │ │ └── navigation.theme.scss
│ │ │ ├── language-selector
│ │ │ │ ├── index.ts
│ │ │ │ ├── language-selector.component.scss
│ │ │ │ ├── language-selector.component.html
│ │ │ │ ├── language-selector.theme.scss
│ │ │ │ └── language-selector.component.ts
│ │ │ ├── simple-form-error
│ │ │ │ ├── index.ts
│ │ │ │ ├── simple-form-error.component.html
│ │ │ │ └── simple-form-error.component.ts
│ │ │ ├── translated-form-error
│ │ │ │ ├── index.ts
│ │ │ │ ├── translated-form-error.component.html
│ │ │ │ └── translated-form-error.component.ts
│ │ │ └── index.ts
│ │ ├── pages
│ │ │ ├── ngx-forms-example
│ │ │ │ ├── index.ts
│ │ │ │ ├── ngx-forms-example.component.scss
│ │ │ │ └── ngx-forms-example.component.ts
│ │ │ ├── reactive-forms-example
│ │ │ │ ├── index.ts
│ │ │ │ ├── reactive-forms-example.component.scss
│ │ │ │ └── reactive-forms-example.component.ts
│ │ │ ├── template-driven-forms-example
│ │ │ │ ├── index.ts
│ │ │ │ ├── template-driven-forms-example.component.scss
│ │ │ │ └── template-driven-forms-example.component.ts
│ │ │ ├── typed-reactive-forms-example
│ │ │ │ ├── index.ts
│ │ │ │ ├── matching-password.ts
│ │ │ │ ├── typed-reactive-forms-example.component.scss
│ │ │ │ └── typed-reactive-forms-example.component.ts
│ │ │ └── index.ts
│ │ ├── app.component.scss
│ │ ├── parent-error-state-matcher.ts
│ │ ├── translation.config.ts
│ │ ├── app-routing.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── password-validator.ts
│ │ ├── app.component.ts
│ │ └── app.module.ts
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── styles
│ │ ├── _variables.scss
│ │ ├── styles.scss
│ │ └── _app.theme.scss
│ ├── index.html
│ ├── main.ts
│ ├── test.ts
│ ├── assets
│ │ ├── img
│ │ │ └── github-icon.svg
│ │ └── translations
│ │ │ ├── en.json
│ │ │ ├── nl.json
│ │ │ └── fr.json
│ └── polyfills.ts
│ ├── e2e
│ ├── tsconfig.json
│ ├── src
│ │ ├── app.po.ts
│ │ └── app.e2e-spec.ts
│ └── protractor.conf.js
│ ├── tsconfig.spec.json
│ ├── .editorconfig
│ ├── tsconfig.app.json
│ ├── .browserslistrc
│ ├── .eslintrc.json
│ ├── tsconfig.json
│ ├── .gitignore
│ ├── karma.conf.js
│ ├── README.md
│ ├── package.json
│ └── angular.json
├── src
├── services.ts
├── directives.ts
├── ngx-form-errors.ts
├── form-errors-config.intf.ts
├── form-error-component.intf.ts
├── form-error.intf.ts
├── directives
│ ├── form-errors-group.directive.ts
│ └── form-errors-group.directive.spec.ts
├── form-errors.module.ts
└── services
│ └── form-errors-message.service.ts
├── .prettierrc.js
├── docs
└── summary.json
├── stylelint.config.js
├── tsconfig.spec.json
├── .gitattributes
├── .prettierignore
├── ng-package.json
├── .github
├── ISSUE_TEMPLATE
│ ├── 05-other.md
│ ├── 02-feature_request.md
│ ├── 04-support.md
│ ├── 03-documentation.md
│ └── 01-bug.md
└── PULL_REQUEST_TEMPLATE.md
├── public_api.ts
├── tsconfig.lib.prod.json
├── .compodocrc.json
├── tsconfig.json
├── scripts
├── ci
│ ├── print-logs.sh
│ └── _ghactions-group.sh
└── helpers.js
├── commitlint.config.js
├── tsconfig.lib.json
├── .eslintrc.json
├── .editorconfig
├── base.spec.ts
├── .npmignore
├── .gitignore
├── .release-it.json
├── CODE_OF_CONDUCT.md
├── angular.json
├── .cz-config.js
├── LICENSE
├── util-functions.sh
├── RELEASE.md
├── karma.conf.js
└── release-publish.sh
/.nvmrc:
--------------------------------------------------------------------------------
1 | 12.22.1
2 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | commitlint --edit $1
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.org
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | lint-staged && npm run docs:coverage
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.org
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.org
2 |
--------------------------------------------------------------------------------
/src/services.ts:
--------------------------------------------------------------------------------
1 | export * from "./services/form-errors-message.service";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/card/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./card.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/card/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./card.component";
2 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = require("@nationalbankbelgium/code-style/prettier/3.1.x");
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./navigation.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./navigation.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/ngx-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./ngx-forms-example.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/ngx-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./ngx-forms-example.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/language-selector/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./language-selector.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/simple-form-error/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./simple-form-error.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/language-selector/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./language-selector.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/simple-form-error/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./simple-form-error.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/docs/summary.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Developer Guide",
4 | "file": "DEV_GUIDE.md"
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/translated-form-error/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./translated-form-error.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/reactive-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./reactive-forms-example.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/translated-form-error/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./translated-form-error.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/reactive-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./reactive-forms-example.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/card/card.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/card/card.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/template-driven-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./template-driven-forms-example.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NationalBankBelgium/ngx-form-errors/HEAD/demo-app/ng15/src/favicon.ico
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/template-driven-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./template-driven-forms-example.component";
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NationalBankBelgium/ngx-form-errors/HEAD/demo-app/ng16/src/favicon.ico
--------------------------------------------------------------------------------
/src/directives.ts:
--------------------------------------------------------------------------------
1 | export * from "./directives/form-errors-group.directive";
2 | export * from "./directives/form-errors.directive";
3 |
--------------------------------------------------------------------------------
/stylelint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["@nationalbankbelgium/code-style/stylelint/13.13.x", "stylelint-config-prettier"]
3 | };
4 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/card/card.component.scss:
--------------------------------------------------------------------------------
1 | :host mat-card {
2 | box-sizing: border-box;
3 | width: 100%;
4 | min-height: 100%;
5 | }
6 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/card/card.component.scss:
--------------------------------------------------------------------------------
1 | :host mat-card {
2 | box-sizing: border-box;
3 | width: 100%;
4 | min-height: 100%;
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {},
4 | "files": null,
5 | "include": ["*.ts", "src/**/*.ts"]
6 | }
7 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/navigation/navigation.component.scss:
--------------------------------------------------------------------------------
1 | :host mat-nav-list {
2 | padding: 0;
3 |
4 | a {
5 | outline: none;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/typed-reactive-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./typed-reactive-forms-example.component";
2 | export * from "./matching-password";
3 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/navigation/navigation.component.scss:
--------------------------------------------------------------------------------
1 | :host mat-nav-list {
2 | padding: 0;
3 |
4 | a {
5 | outline: none;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/typed-reactive-forms-example/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./typed-reactive-forms-example.component";
2 | export * from "./matching-password";
3 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/typed-reactive-forms-example/matching-password.ts:
--------------------------------------------------------------------------------
1 | export interface MatchingPassword {
2 | password: string;
3 | confirmPassword: string;
4 | }
5 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/typed-reactive-forms-example/matching-password.ts:
--------------------------------------------------------------------------------
1 | export interface MatchingPassword {
2 | password: string;
3 | confirmPassword: string;
4 | }
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # JS and TS files must always use LF for tools to work
5 | *.js eol=lf
6 | *.ts eol=lf
7 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .angular/
2 | .git/
3 | .idea/
4 | .vscode/
5 | dist/
6 | coverage/
7 | reports/
8 | node_modules/
9 | CHANGELOG.md
10 | package.json
11 | package-lock.json
12 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/styles/_variables.scss:
--------------------------------------------------------------------------------
1 | $monitor-query: "screen and (min-width:1200px)";
2 | $table-query: "screen and (max-width: 1200px)";
3 | $mobile-query: "screen and (max-width: 600px)";
4 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/styles/_variables.scss:
--------------------------------------------------------------------------------
1 | $monitor-query: "screen and (min-width:1200px)";
2 | $table-query: "screen and (max-width: 1200px)";
3 | $mobile-query: "screen and (max-width: 600px)";
4 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/simple-form-error/simple-form-error.component.html:
--------------------------------------------------------------------------------
1 |
{{ error.message }}
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/simple-form-error/simple-form-error.component.html:
--------------------------------------------------------------------------------
1 | {{ error.message }}
2 |
--------------------------------------------------------------------------------
/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json",
3 | "lib": {
4 | "entryFile": "public_api.ts",
5 | "flatModuleFile": "ngx-form-errors"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/05-other.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '💭 Other'
3 | about: Issues that don't fit under anything else
4 | title: ''
5 | labels: ''
6 | ---
7 |
8 | ## What?
9 |
10 | ## When?
11 |
12 | ## How?
--------------------------------------------------------------------------------
/public_api.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Entry point for all public APIs of this package.
3 | */
4 | export * from "./src/ngx-form-errors";
5 |
6 | // This file only reexports content of the `src` folder. Keep it that way.
7 |
--------------------------------------------------------------------------------
/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "compilerOptions": {
4 | "declarationMap": false
5 | },
6 | "angularCompilerOptions": {
7 | "enableIvy": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./ngx-forms-example";
2 | export * from "./reactive-forms-example";
3 | export * from "./template-driven-forms-example";
4 | export * from "./typed-reactive-forms-example";
5 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./ngx-forms-example";
2 | export * from "./reactive-forms-example";
3 | export * from "./template-driven-forms-example";
4 | export * from "./typed-reactive-forms-example";
5 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./card";
2 | export * from "./language-selector";
3 | export * from "./navigation";
4 | export * from "./simple-form-error";
5 | export * from "./translated-form-error";
6 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./card";
2 | export * from "./language-selector";
3 | export * from "./navigation";
4 | export * from "./simple-form-error";
5 | export * from "./translated-form-error";
6 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/translated-form-error/translated-form-error.component.html:
--------------------------------------------------------------------------------
1 | {{ error.message | translate: error.params }}
2 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/translated-form-error/translated-form-error.component.html:
--------------------------------------------------------------------------------
1 | {{ error.message | translate: error.params }}
2 |
--------------------------------------------------------------------------------
/src/ngx-form-errors.ts:
--------------------------------------------------------------------------------
1 | export * from "./directives";
2 | export * from "./services";
3 | export * from "./form-error.intf";
4 | export * from "./form-error-component.intf";
5 | export * from "./form-errors-config.intf";
6 | export * from "./form-errors.module";
7 |
--------------------------------------------------------------------------------
/demo-app/ng15/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es2018",
7 | "types": ["jasmine", "jasminewd2", "node"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/language-selector/language-selector.component.scss:
--------------------------------------------------------------------------------
1 | :host mat-button-toggle-group {
2 | box-sizing: border-box;
3 | width: 100%;
4 | border-radius: 0;
5 |
6 | mat-button-toggle.accent {
7 | flex: 1;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo-app/ng16/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es2018",
7 | "types": ["jasmine", "jasminewd2", "node"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/language-selector/language-selector.component.scss:
--------------------------------------------------------------------------------
1 | :host mat-button-toggle-group {
2 | box-sizing: border-box;
3 | width: 100%;
4 | border-radius: 0;
5 |
6 | mat-button-toggle.accent {
7 | flex: 1;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.compodocrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@compodoc/compodoc/src/config/schema.json",
3 | "theme": "material",
4 | "tsconfig": "./tsconfig.json",
5 | "output": "./reports/api-docs/ngx-form-errors",
6 | "includes": "./docs",
7 | "includesName": "Developer Guide"
8 | }
9 |
--------------------------------------------------------------------------------
/demo-app/ng15/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["src/test.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/demo-app/ng16/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["src/test.ts", "src/polyfills.ts"],
8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@nationalbankbelgium/code-style/tsconfig/5.1.x/ng16",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "rootDir": ".",
6 | "typeRoots": ["./node_modules/@types"],
7 | "lib": ["dom", "dom.iterable", "es2017"],
8 | "paths": {},
9 | "outDir": "./dist"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo-app/ng15/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/demo-app/ng16/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/demo-app/ng15/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from "protractor";
2 |
3 | export class AppPage {
4 | public navigateTo() {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | public getTitleText() {
9 | return element(by.css("app-root .content span")).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo-app/ng16/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from "protractor";
2 |
3 | export class AppPage {
4 | public navigateTo() {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | public getTitleText() {
9 | return element(by.css("app-root .content span")).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/styles/styles.scss:
--------------------------------------------------------------------------------
1 | @import "~material-design-icons/iconfont/material-icons.css";
2 | @import "app.theme";
3 |
4 | app-root {
5 | height: 100vh;
6 | display: flex;
7 | flex-direction: column;
8 | }
9 |
10 | .container {
11 | margin: 20px auto;
12 | padding: 20px;
13 | box-sizing: border-box;
14 | max-width: 1200px;
15 | }
16 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/styles/styles.scss:
--------------------------------------------------------------------------------
1 | @import "~material-design-icons/iconfont/material-icons.css";
2 | @import "app.theme";
3 |
4 | app-root {
5 | height: 100vh;
6 | display: flex;
7 | flex-direction: column;
8 | }
9 |
10 | .container {
11 | margin: 20px auto;
12 | padding: 20px;
13 | box-sizing: border-box;
14 | max-width: 1200px;
15 | }
16 |
--------------------------------------------------------------------------------
/demo-app/ng15/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/app",
5 | "types": ["node"],
6 | "paths": {
7 | "@angular/*": ["./node_modules/@angular/*"]
8 | }
9 | },
10 | "files": ["src/main.ts", "src/polyfills.ts"],
11 | "include": ["src/**/*.d.ts"],
12 | "exclude": ["src/test.ts", "src/**/*.spec.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/demo-app/ng16/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/app",
5 | "types": ["node"],
6 | "paths": {
7 | "@angular/*": ["./node_modules/@angular/*"]
8 | }
9 | },
10 | "files": ["src/main.ts", "src/polyfills.ts"],
11 | "include": ["src/**/*.d.ts"],
12 | "exclude": ["src/test.ts", "src/**/*.spec.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NgxFormErrors Showcase
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NgxFormErrors Showcase
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/navigation/navigation.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, HostBinding } from "@angular/core";
2 |
3 | const componentName = "navigation";
4 |
5 | @Component({
6 | selector: "app-navigation",
7 | templateUrl: "./navigation.component.html",
8 | styleUrls: ["./navigation.component.scss"]
9 | })
10 | export class NavigationComponent {
11 | @HostBinding("class")
12 | public cssClass: string = componentName;
13 | }
14 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/navigation/navigation.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, HostBinding } from "@angular/core";
2 |
3 | const componentName = "navigation";
4 |
5 | @Component({
6 | selector: "app-navigation",
7 | templateUrl: "./navigation.component.html",
8 | styleUrls: ["./navigation.component.scss"]
9 | })
10 | export class NavigationComponent {
11 | @HostBinding("class")
12 | public cssClass: string = componentName;
13 | }
14 |
--------------------------------------------------------------------------------
/scripts/ci/print-logs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -u -e -o pipefail
4 |
5 | # Setup environment
6 | readonly thisDir=$(cd $(dirname $0); pwd)
7 | source ${thisDir}/_ghactions-group.sh
8 |
9 |
10 | for FILE in ${LOGS_DIR}/*; do
11 | ghActionsGroupStart "print log file: ${FILE}"
12 | cat $FILE
13 | ghActionsGroupEnd "print log file: ${FILE}"
14 | done
15 |
16 | # Print return arrows as a log separator
17 | ghActionsGroupReturnArrows
18 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from "@angular/core";
2 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
3 |
4 | import { AppModule } from "./app/app.module";
5 | import { environment } from "./environments/environment";
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch((err: any) => console.error(err));
14 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from "@angular/core";
2 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
3 |
4 | import { AppModule } from "./app/app.module";
5 | import { environment } from "./environments/environment";
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch((err: any) => console.error(err));
14 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | mat-toolbar {
2 | .slogan {
3 | font-size: 16px;
4 | font-style: italic;
5 | line-height: normal;
6 | }
7 |
8 | .spacer {
9 | flex: 1 1 auto;
10 | }
11 | }
12 |
13 | mat-sidenav-container {
14 | flex: 100% 1;
15 |
16 | mat-sidenav {
17 | max-height: 100%;
18 | overflow-y: auto;
19 | }
20 |
21 | mat-sidenav-content {
22 | max-height: 100%;
23 | overflow-y: auto;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | mat-toolbar {
2 | .slogan {
3 | font-size: 16px;
4 | font-style: italic;
5 | line-height: normal;
6 | }
7 |
8 | .spacer {
9 | flex: 1 1 auto;
10 | }
11 | }
12 |
13 | mat-sidenav-container {
14 | flex: 100% 1;
15 |
16 | mat-sidenav {
17 | max-height: 100%;
18 | overflow-y: auto;
19 | }
20 |
21 | mat-sidenav-content {
22 | max-height: 100%;
23 | overflow-y: auto;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/demo-app/ng15/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
13 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/navigation/navigation.component.html:
--------------------------------------------------------------------------------
1 |
2 | Template Driven Forms
3 | Reactive Forms
4 | Ngx Form Errors
5 | Typed Reactive Form
6 |
7 |
--------------------------------------------------------------------------------
/demo-app/ng16/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
13 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/navigation/navigation.component.html:
--------------------------------------------------------------------------------
1 |
2 | Template Driven Forms
3 | Reactive Forms
4 | Ngx Form Errors
5 | Typed Reactive Form
6 |
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/02-feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '🚀 Feature request'
3 | about: Request a new feature
4 | title: ''
5 | labels: 'enhancement'
6 | ---
7 |
8 | # 🚀 Feature request
9 |
10 | ## Current behavior
11 |
12 |
13 |
14 | ## Expected behavior
15 |
16 |
17 |
18 | ## What is the motivation / use case for changing the behavior?
19 |
20 |
21 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["@commitlint/config-conventional"],
3 |
4 | //See here for the rules definition : https://github.com/marionebl/commitlint/blob/master/docs/reference-rules.md
5 | rules: {
6 | "header-max-length": [1, "always", 100],
7 | "scope-enum": [
8 | 2,
9 | "always",
10 | ["accessibility", "build", "developer-guide", "docs", "qa", "release", "all", "service", "directive", "demo"]
11 | ],
12 | "scope-case": [2, "always", "lowerCase"]
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/language-selector/language-selector.component.html:
--------------------------------------------------------------------------------
1 |
2 |
10 | {{ language | uppercase }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/language-selector/language-selector.component.html:
--------------------------------------------------------------------------------
1 |
2 |
10 | {{ language | uppercase }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "angularCompilerOptions": {
4 | "annotateForClosureCompiler": true,
5 | "skipTemplateCodegen": true,
6 | "enableResourceInlining": true
7 | },
8 | "buildOnSave": false,
9 | "compileOnSave": false,
10 | "compilerOptions": {
11 | "outDir": "AUTOGENERATED",
12 | "declarationMap": true,
13 | "declarationDir": "AUTOGENERATED",
14 | "inlineSourceMap": true
15 | },
16 | "exclude": ["node_modules", "dist", "demo-app", "**/*.spec.ts"]
17 | }
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/04-support.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '🚑 Support'
3 | about: Questions and requests for support
4 | title: ''
5 | labels: 'question'
6 | ---
7 |
8 | 🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
9 |
10 | Please do not file questions or support requests on the GitHub issues tracker.
11 |
12 | You can get your questions answered using other communication channels. Please see: https://github.com/NationalBankBelgium/ngx-form-errors/blob/master/CONTRIBUTING.md#got-a-question-or-problem
13 |
14 | Thank you!
15 |
16 | 🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
17 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | /* eslint-disable import/no-unassigned-import */
4 | import "zone.js/testing";
5 | import { getTestBed } from "@angular/core/testing";
6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing";
7 |
8 | // First, initialize the Angular testing environment.
9 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
10 | teardown: { destroyAfterEach: false }
11 | });
12 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | /* eslint-disable import/no-unassigned-import */
4 | import "zone.js/testing";
5 | import { getTestBed } from "@angular/core/testing";
6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing";
7 |
8 | // First, initialize the Angular testing environment.
9 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
10 | teardown: { destroyAfterEach: false }
11 | });
12 |
--------------------------------------------------------------------------------
/demo-app/ng15/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["@nationalbankbelgium"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "rules": {
8 | "@angular-eslint/component-selector": [
9 | "error",
10 | {
11 | "prefix": "app",
12 | "style": "kebab-case",
13 | "type": "element"
14 | }
15 | ],
16 | "@angular-eslint/directive-selector": [
17 | "error",
18 | {
19 | "prefix": "app",
20 | "style": "camelCase",
21 | "type": "attribute"
22 | }
23 | ]
24 | }
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/demo-app/ng16/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["@nationalbankbelgium"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "rules": {
8 | "@angular-eslint/component-selector": [
9 | "error",
10 | {
11 | "prefix": "app",
12 | "style": "kebab-case",
13 | "type": "element"
14 | }
15 | ],
16 | "@angular-eslint/directive-selector": [
17 | "error",
18 | {
19 | "prefix": "app",
20 | "style": "camelCase",
21 | "type": "attribute"
22 | }
23 | ]
24 | }
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["@nationalbankbelgium"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "rules": {
8 | "@angular-eslint/component-selector": [
9 | "error",
10 | {
11 | "prefix": "ngx-form-errors",
12 | "style": "kebab-case",
13 | "type": "element"
14 | }
15 | ],
16 | "@angular-eslint/directive-selector": [
17 | "error",
18 | {
19 | "prefix": "ngxFormErrors",
20 | "style": "camelCase",
21 | "type": "attribute"
22 | }
23 | ]
24 | }
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs.
2 | # More information at http://editorconfig.org
3 |
4 | # top-most EditorConfig file
5 | root = true
6 |
7 | # Unix-style newlines with a newline ending every file.
8 | # Tab for indentation, whitespace trimming and UTF-8 encoding
9 | [*]
10 | end_of_line = lf
11 | insert_final_newline = true
12 | indent_style = tab
13 | trim_trailing_whitespace = false
14 | charset = utf-8
15 |
16 | [*.bat]
17 | end_of_line = crlf
18 |
19 | [**.{css,pcss,scss,json,sh,yml}]
20 | indent_style = space
21 | indent_size = 2
22 |
23 | [*.md]
24 | insert_final_newline = false
25 | trim_trailing_whitespace = false
26 |
27 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/parent-error-state-matcher.ts:
--------------------------------------------------------------------------------
1 | import { UntypedFormControl, FormGroupDirective, NgForm } from "@angular/forms";
2 | import { ErrorStateMatcher } from "@angular/material/core";
3 |
4 | /** Error when invalid control is dirty, touched, or submitted. */
5 | export class ParentErrorStateMatcher implements ErrorStateMatcher {
6 | public isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
7 | const isSubmitted = !!(form && form.submitted);
8 | const formGroupValid = !!(form && form.valid);
9 |
10 | return !!control && (control.invalid || !formGroupValid) && (control.dirty || control.touched || isSubmitted);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/parent-error-state-matcher.ts:
--------------------------------------------------------------------------------
1 | import { UntypedFormControl, FormGroupDirective, NgForm } from "@angular/forms";
2 | import { ErrorStateMatcher } from "@angular/material/core";
3 |
4 | /** Error when invalid control is dirty, touched, or submitted. */
5 | export class ParentErrorStateMatcher implements ErrorStateMatcher {
6 | public isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
7 | const isSubmitted = !!(form && form.submitted);
8 | const formGroupValid = !!(form && form.valid);
9 |
10 | return !!control && (control.invalid || !formGroupValid) && (control.dirty || control.touched || isSubmitted);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/navigation/navigation.theme.scss:
--------------------------------------------------------------------------------
1 | @use "@angular/material" as mat;
2 | @mixin app-navigation-theme($theme) {
3 | $primary: map-get($theme, primary);
4 | $accent: map-get($theme, accent);
5 | .navigation mat-nav-list a.mat-list-item {
6 | &.active {
7 | &,
8 | &:hover,
9 | &:active {
10 | background-color: mat.get-color-from-palette($primary, 500);
11 | color: mat.get-contrast-color-from-palette($primary, 500);
12 | }
13 | }
14 |
15 | &:hover {
16 | background-color: mat.get-color-from-palette($primary, 100);
17 | color: mat.get-contrast-color-from-palette($primary, 100);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/navigation/navigation.theme.scss:
--------------------------------------------------------------------------------
1 | @use "@angular/material" as mat;
2 | @mixin app-navigation-theme($theme) {
3 | $primary: map-get($theme, primary);
4 | $accent: map-get($theme, accent);
5 | .navigation mat-nav-list a.mat-list-item {
6 | &.active {
7 | &,
8 | &:hover,
9 | &:active {
10 | background-color: mat.get-color-from-palette($primary, 500);
11 | color: mat.get-contrast-color-from-palette($primary, 500);
12 | }
13 | }
14 |
15 | &:hover {
16 | background-color: mat.get-color-from-palette($primary, 100);
17 | color: mat.get-contrast-color-from-palette($primary, 100);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/language-selector/language-selector.theme.scss:
--------------------------------------------------------------------------------
1 | @use "@angular/material" as mat;
2 | @mixin language-selector-theme($theme) {
3 | $primary: map-get($theme, primary);
4 |
5 | .language-selector mat-button-toggle-group mat-button-toggle {
6 | &.active {
7 | &,
8 | &:hover,
9 | &:active {
10 | background-color: mat.get-color-from-palette($primary, 500);
11 | color: mat.get-contrast-color-from-palette($primary, 500);
12 | }
13 | }
14 |
15 | &:hover,
16 | &:focus {
17 | background-color: mat.get-color-from-palette($primary, 100);
18 | color: mat.get-contrast-color-from-palette($primary, 100);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/language-selector/language-selector.theme.scss:
--------------------------------------------------------------------------------
1 | @use "@angular/material" as mat;
2 | @mixin language-selector-theme($theme) {
3 | $primary: map-get($theme, primary);
4 |
5 | .language-selector mat-button-toggle-group mat-button-toggle {
6 | &.active {
7 | &,
8 | &:hover,
9 | &:active {
10 | background-color: mat.get-color-from-palette($primary, 500);
11 | color: mat.get-contrast-color-from-palette($primary, 500);
12 | }
13 | }
14 |
15 | &:hover,
16 | &:focus {
17 | background-color: mat.get-color-from-palette($primary, 100);
18 | color: mat.get-contrast-color-from-palette($primary, 100);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/demo-app/ng15/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@nationalbankbelgium/code-style/tsconfig/4.9.x/ng15",
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "rootDir": "../",
7 | "outDir": "./dist/out-tsc",
8 | "declaration": false,
9 | "downlevelIteration": true,
10 | "experimentalDecorators": true,
11 | "module": "es2020",
12 | "moduleResolution": "node",
13 | "importHelpers": true,
14 | "target": "es2022",
15 | "typeRoots": ["node_modules/@types"],
16 | "lib": ["dom", "dom.iterable", "es2022"],
17 | "useDefineForClassFields": false
18 | },
19 | "angularCompilerOptions": {
20 | "fullTemplateTypeCheck": true,
21 | "strictInjectionParameters": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demo-app/ng16/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@nationalbankbelgium/code-style/tsconfig/5.1.x/ng16",
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "rootDir": "../",
7 | "outDir": "./dist/out-tsc",
8 | "declaration": false,
9 | "downlevelIteration": true,
10 | "experimentalDecorators": true,
11 | "module": "es2020",
12 | "moduleResolution": "node",
13 | "importHelpers": true,
14 | "target": "ES2022",
15 | "typeRoots": ["node_modules/@types"],
16 | "lib": ["dom", "dom.iterable", "es2022"],
17 | "useDefineForClassFields": false
18 | },
19 | "angularCompilerOptions": {
20 | "fullTemplateTypeCheck": true,
21 | "strictInjectionParameters": true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/translation.config.ts:
--------------------------------------------------------------------------------
1 | import { TranslateService } from "@ngx-translate/core";
2 |
3 | const translationsEn: object = require("../assets/translations/en.json");
4 | const translationsFr: object = require("../assets/translations/fr.json");
5 | const translationsNl: object = require("../assets/translations/nl.json");
6 |
7 | /**
8 | * @param translateService - The translation service
9 | */
10 | export function initializeTranslation(translateService: TranslateService): void {
11 | translateService.addLangs(["en", "fr", "nl"]);
12 | translateService.setDefaultLang("en");
13 | translateService.use("en");
14 |
15 | translateService.setTranslation("en", translationsEn);
16 | translateService.setTranslation("fr", translationsFr);
17 | translateService.setTranslation("nl", translationsNl);
18 | }
19 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/translation.config.ts:
--------------------------------------------------------------------------------
1 | import { TranslateService } from "@ngx-translate/core";
2 |
3 | const translationsEn: object = require("../assets/translations/en.json");
4 | const translationsFr: object = require("../assets/translations/fr.json");
5 | const translationsNl: object = require("../assets/translations/nl.json");
6 |
7 | /**
8 | * @param translateService - The translation service
9 | */
10 | export function initializeTranslation(translateService: TranslateService): void {
11 | translateService.addLangs(["en", "fr", "nl"]);
12 | translateService.setDefaultLang("en");
13 | translateService.use("en");
14 |
15 | translateService.setTranslation("en", translationsEn);
16 | translateService.setTranslation("fr", translationsFr);
17 | translateService.setTranslation("nl", translationsNl);
18 | }
19 |
--------------------------------------------------------------------------------
/base.spec.ts:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /* tslint:disable:no-import-side-effect */
4 | import "core-js/es6";
5 | import "core-js/es7/reflect";
6 | import "core-js/stage/4";
7 |
8 | import "zone.js";
9 | import "zone.js/dist/long-stack-trace-zone";
10 | import "zone.js/dist/proxy"; // since zone.js 0.6.15
11 | import "zone.js/dist/sync-test";
12 | import "zone.js/dist/jasmine-patch"; // put here since zone.js 0.6.14
13 | import "zone.js/dist/async-test";
14 | import "zone.js/dist/fake-async-test";
15 | /* tslint:enable */
16 |
17 | import { TestBed } from "@angular/core/testing";
18 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing";
19 |
20 | TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: false } });
21 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/assets/img/github-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/assets/img/github-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/demo-app/ng15/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from "./app.po";
2 | import { browser, logging } from "protractor";
3 |
4 | describe("workspace-project App", () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it("should display welcome message", async () => {
12 | await page.navigateTo();
13 | const titleText = await page.getTitleText();
14 | expect(titleText).toEqual("demo-app app is running!");
15 | });
16 |
17 | afterEach(async () => {
18 | // Assert that there are no errors emitted from the browser
19 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
20 | const expectedLogEntry: Partial = {
21 | level: logging.Level.SEVERE
22 | };
23 | expect(logs).not.toContain(jasmine.objectContaining(expectedLogEntry));
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo-app/ng16/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from "./app.po";
2 | import { browser, logging } from "protractor";
3 |
4 | describe("workspace-project App", () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it("should display welcome message", async () => {
12 | await page.navigateTo();
13 | const titleText = await page.getTitleText();
14 | expect(titleText).toEqual("demo-app app is running!");
15 | });
16 |
17 | afterEach(async () => {
18 | // Assert that there are no errors emitted from the browser
19 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
20 | const expectedLogEntry: Partial = {
21 | level: logging.Level.SEVERE
22 | };
23 | expect(logs).not.toContain(jasmine.objectContaining(expectedLogEntry));
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/form-errors-config.intf.ts:
--------------------------------------------------------------------------------
1 | import { InjectionToken, Type } from "@angular/core";
2 | import { NgxFormErrorComponent } from "./form-error-component.intf";
3 |
4 | /**
5 | * The InjectionToken version of the config name
6 | */
7 | export const NGX_FORM_ERRORS_CONFIG: InjectionToken = new InjectionToken("NgxFormErrorsConfig");
8 |
9 | /**
10 | * Definition of the configuration object for the {@link NgxFormErrorsModule}
11 | */
12 | export interface NgxFormErrorsConfig {
13 | /**
14 | * Error component to be dynamically created by the ngxFormErrors directive which will display the validation errors.
15 | * This component should implement the {@link NgxFormErrorComponent} interface.
16 | */
17 | formErrorComponent: Type;
18 | }
19 |
--------------------------------------------------------------------------------
/demo-app/ng15/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.angular/cache
36 | /.sass-cache
37 | /connect.lock
38 | /coverage
39 | /libpeerconnection.log
40 | npm-debug.log
41 | yarn-error.log
42 | testem.log
43 | /typings
44 |
45 | # System Files
46 | .DS_Store
47 | Thumbs.db
48 |
--------------------------------------------------------------------------------
/demo-app/ng16/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.angular/cache
36 | /.sass-cache
37 | /connect.lock
38 | /coverage
39 | /libpeerconnection.log
40 | npm-debug.log
41 | yarn-error.log
42 | testem.log
43 | /typings
44 |
45 | # System Files
46 | .DS_Store
47 | Thumbs.db
48 |
--------------------------------------------------------------------------------
/src/form-error-component.intf.ts:
--------------------------------------------------------------------------------
1 | import { NgxFormFieldError } from "./form-error.intf";
2 | import { Observable } from "rxjs";
3 |
4 | /**
5 | * Describes an Error component to be dynamically created by the ngxFormErrors directive.
6 | * `IMPORTANT:` The Error component should be provided in the {@link NgxFormErrorsModule}.forRoot() method and must implement this interface.
7 | */
8 | export interface NgxFormErrorComponent {
9 | /**
10 | * An observable that emits the validation errors of the form control whenever the control changes
11 | */
12 | errors$: Observable;
13 |
14 | /**
15 | * Method to be called by the ngxFormErrors directive only.
16 | * It must contain the necessary logic to subscribe to the errors$ observable to update the current validation errors
17 | * from the form control
18 | */
19 | subscribeToErrors(): void;
20 | }
21 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/simple-form-error/simple-form-error.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, HostBinding } from "@angular/core";
2 | import { Observable } from "rxjs";
3 | import { NgxFormErrorComponent, NgxFormFieldError } from "@nationalbankbelgium/ngx-form-errors";
4 |
5 | @Component({
6 | selector: "app-simple-form-error",
7 | templateUrl: "./simple-form-error.component.html"
8 | })
9 | export class SimpleFormErrorComponent implements NgxFormErrorComponent {
10 | @HostBinding("class")
11 | public cssClass = "simple-form-error";
12 |
13 | public errors: NgxFormFieldError[] = [];
14 | public errors$!: Observable;
15 |
16 | public subscribeToErrors(): void {
17 | this.errors$.subscribe((errors: NgxFormFieldError[]) => {
18 | this.errors = errors;
19 | });
20 | }
21 |
22 | public trackError(index: number): number {
23 | return index;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/simple-form-error/simple-form-error.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, HostBinding } from "@angular/core";
2 | import { Observable } from "rxjs";
3 | import { NgxFormErrorComponent, NgxFormFieldError } from "@nationalbankbelgium/ngx-form-errors";
4 |
5 | @Component({
6 | selector: "app-simple-form-error",
7 | templateUrl: "./simple-form-error.component.html"
8 | })
9 | export class SimpleFormErrorComponent implements NgxFormErrorComponent {
10 | @HostBinding("class")
11 | public cssClass = "simple-form-error";
12 |
13 | public errors: NgxFormFieldError[] = [];
14 | public errors$!: Observable;
15 |
16 | public subscribeToErrors(): void {
17 | this.errors$.subscribe((errors: NgxFormFieldError[]) => {
18 | this.errors = errors;
19 | });
20 | }
21 |
22 | public trackError(index: number): number {
23 | return index;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/form-error.intf.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Error object describing the validation errors from a form control
3 | */
4 | export interface NgxFormFieldError {
5 | /**
6 | * The type of validation (Angular validator name)
7 | */
8 | error: string;
9 |
10 | /**
11 | * The name of the FormControl whose validation has failed
12 | */
13 | formControlName: string;
14 |
15 | /**
16 | * The validation error message
17 | */
18 | message: string;
19 |
20 | /**
21 | * Additional parameters
22 | */
23 | params: {
24 | /**
25 | * Alias of the validated field. Defined via the "ngxFormErrorsFieldName" attribute of the {@link NgxFormErrorsDirective}
26 | * If no alias is defined for the field then this is the FormControl's name
27 | */
28 | fieldName: string;
29 | /**
30 | * Any parameters passed to the actual Angular validator
31 | */
32 | [p: string]: any;
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from "@angular/core";
2 | import { RouterModule, Routes } from "@angular/router";
3 | import {
4 | NgxFormsExampleComponent,
5 | ReactiveFormsExampleComponent,
6 | TemplateDrivenFormsExampleComponent,
7 | TypedReactiveFormsExampleComponent
8 | } from "./pages";
9 |
10 | const routes: Routes = [
11 | { path: "", redirectTo: "/template-driven-forms", pathMatch: "full" },
12 | { path: "reactive-forms", component: ReactiveFormsExampleComponent },
13 | { path: "template-driven-forms", component: TemplateDrivenFormsExampleComponent },
14 | { path: "ngx-form-errors", component: NgxFormsExampleComponent },
15 | { path: "typed-reactive-forms", component: TypedReactiveFormsExampleComponent }
16 | ];
17 |
18 | @NgModule({
19 | imports: [RouterModule.forRoot(routes)],
20 | exports: [RouterModule]
21 | })
22 | export class AppRoutingModule {}
23 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from "@angular/core";
2 | import { RouterModule, Routes } from "@angular/router";
3 | import {
4 | NgxFormsExampleComponent,
5 | ReactiveFormsExampleComponent,
6 | TemplateDrivenFormsExampleComponent,
7 | TypedReactiveFormsExampleComponent
8 | } from "./pages";
9 |
10 | const routes: Routes = [
11 | { path: "", redirectTo: "/template-driven-forms", pathMatch: "full" },
12 | { path: "reactive-forms", component: ReactiveFormsExampleComponent },
13 | { path: "template-driven-forms", component: TemplateDrivenFormsExampleComponent },
14 | { path: "ngx-form-errors", component: NgxFormsExampleComponent },
15 | { path: "typed-reactive-forms", component: TypedReactiveFormsExampleComponent }
16 | ];
17 |
18 | @NgModule({
19 | imports: [RouterModule.forRoot(routes)],
20 | exports: [RouterModule]
21 | })
22 | export class AppRoutingModule {}
23 |
--------------------------------------------------------------------------------
/demo-app/ng15/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require("jasmine-spec-reporter");
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: ["./src/**/*.e2e-spec.ts"],
13 | capabilities: {
14 | browserName: "chrome"
15 | },
16 | directConnect: true,
17 | baseUrl: "http://localhost:4200/",
18 | framework: "jasmine",
19 | jasmineNodeOpts: {
20 | showColors: true,
21 | defaultTimeoutInterval: 30000,
22 | print: function () {}
23 | },
24 | onPrepare() {
25 | require("ts-node").register({
26 | project: require("path").join(__dirname, "./tsconfig.json")
27 | });
28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/demo-app/ng16/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require("jasmine-spec-reporter");
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: ["./src/**/*.e2e-spec.ts"],
13 | capabilities: {
14 | browserName: "chrome"
15 | },
16 | directConnect: true,
17 | baseUrl: "http://localhost:4200/",
18 | framework: "jasmine",
19 | jasmineNodeOpts: {
20 | showColors: true,
21 | defaultTimeoutInterval: 30000,
22 | print: function () {}
23 | },
24 | onPrepare() {
25 | require("ts-node").register({
26 | project: require("path").join(__dirname, "./tsconfig.json")
27 | });
28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
5 | Ngx-Form-Errors
6 | - Validation messages in Reactive Forms made easy
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
5 | Ngx-Form-Errors
6 | - Validation messages in Reactive Forms made easy
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Build
2 | dist/
3 | .awcache/
4 | /dist/
5 | .tmp/
6 | /.tmp/
7 | tmp/
8 | demo-app/
9 |
10 | # Reports directory
11 | reports/
12 |
13 | # Logs
14 | logs/
15 | *.log
16 |
17 | # Runtime data
18 | pids
19 | *.pid
20 | *.seed
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 |
25 | # Bak
26 | *.bak
27 |
28 | # Node
29 | node_modules/
30 | npm-debug.log
31 | /npm-debug.log.*
32 |
33 | # OS generated files #
34 | Desktop.ini
35 | Thumbs.db
36 | .DS_Store
37 | ehthumbs.db
38 | Icon?
39 |
40 | # JetBrains IDEs
41 | *.iml
42 | .idea/
43 | .webstorm/
44 | *.swp
45 |
46 | # IDE - VSCode
47 | .vscode/*
48 | !.vscode/settings.json
49 | !.vscode/tasks.json
50 | !.vscode/launch.json
51 | !.vscode/extensions.json
52 |
53 | # Sublime text
54 | .sublime-gulp.cache
55 |
56 | # Runtime data
57 | pids
58 | *.pid
59 | *.seed
60 |
61 | # Patch files
62 | *.patch
63 |
64 | # Bash
65 | bash.exe.stackdump
66 |
67 | # Angular #
68 | *.ngfactory.ts
69 | *.css.shim.ts
70 | *.ngsummary.json
71 | *.shim.ngstyle.ts
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #cache angular
2 | /.angular/cache
3 | # Build
4 | dist/
5 | /dist/
6 | .awcache/
7 | .tmp/
8 | /.tmp/
9 | tmp/
10 |
11 | # Reports directory
12 | reports/
13 |
14 | # Logs
15 | logs/
16 | *.log
17 |
18 | # Runtime data
19 | pids
20 | *.pid
21 | *.seed
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 |
26 | # Bak
27 | *.bak
28 |
29 | # Node
30 | node_modules/
31 | npm-debug.log
32 | /npm-debug.log.*
33 |
34 | # OS generated files #
35 | Desktop.ini
36 | Thumbs.db
37 | .DS_Store
38 | ehthumbs.db
39 | Icon?
40 |
41 | # JetBrains IDEs
42 | *.iml
43 | .idea/
44 | .webstorm/
45 | *.swp
46 |
47 | # IDE - VSCode
48 | .vscode/*
49 | !.vscode/settings.json
50 | !.vscode/tasks.json
51 | !.vscode/launch.json
52 | !.vscode/extensions.json
53 |
54 | # Sublime text
55 | .sublime-gulp.cache
56 |
57 | # Runtime data
58 | pids
59 | *.pid
60 | *.seed
61 |
62 | # Patch files
63 | *.patch
64 |
65 | # Bash
66 | bash.exe.stackdump
67 |
68 | # Angular #
69 | *.ngfactory.ts
70 | *.css.shim.ts
71 | *.ngsummary.json
72 | *.shim.ngstyle.ts
73 |
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "ci": false,
3 | "dry-run": false,
4 | "verbose": false,
5 | "force": false,
6 | "disable-metrics": true,
7 | "hooks": {
8 | "after:bump": "npm run generate:changelog"
9 | },
10 | "plugins": {
11 | "@release-it/conventional-changelog": {
12 | "preset": "angular"
13 | }
14 | },
15 | "git": {
16 | "changelog": "npm run generate:changelog-recent",
17 | "requireCleanWorkingDir": true,
18 | "requireUpstream": true,
19 | "requireCommits": false,
20 | "commit": true,
21 | "commitMessage": "chore(release): release ${version}",
22 | "commitArgs": "",
23 | "tag": true,
24 | "tagName": "${version}",
25 | "tagAnnotation": "${version}",
26 | "push": true,
27 | "pushArgs": ["--follow-tags"],
28 | "pushRepo": "origin"
29 | },
30 | "npm": {
31 | "publish": false
32 | },
33 | "github": {
34 | "release": true,
35 | "releaseName": "${version}",
36 | "draft": false,
37 | "tokenRef": "GITHUB_TOKEN",
38 | "assets": null,
39 | "host": null
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/demo-app/ng15/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: "",
7 | frameworks: ["jasmine", "@angular-devkit/build-angular"],
8 | plugins: [
9 | require("karma-jasmine"),
10 | require("karma-chrome-launcher"),
11 | require("karma-jasmine-html-reporter"),
12 | require("karma-coverage-istanbul-reporter"),
13 | require("@angular-devkit/build-angular/plugins/karma")
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require("path").join(__dirname, "./coverage"),
20 | reports: ["html", "lcovonly", "text-summary"],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ["progress", "kjhtml"],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ["Chrome"],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/demo-app/ng16/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: "",
7 | frameworks: ["jasmine", "@angular-devkit/build-angular"],
8 | plugins: [
9 | require("karma-jasmine"),
10 | require("karma-chrome-launcher"),
11 | require("karma-jasmine-html-reporter"),
12 | require("karma-coverage-istanbul-reporter"),
13 | require("@angular-devkit/build-angular/plugins/karma")
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require("path").join(__dirname, "./coverage"),
20 | reports: ["html", "lcovonly", "text-summary"],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ["progress", "kjhtml"],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ["Chrome"],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/card/card.theme.scss:
--------------------------------------------------------------------------------
1 | @use "@angular/material" as mat;
2 | @mixin card-theme($theme) {
3 | $primary: map-get($theme, primary);
4 | $accent: map-get($theme, accent);
5 | $warning: map-get($theme, warn);
6 | $success: map-get($theme, success);
7 |
8 | app-card.app-color-primary mat-card {
9 | background-color: mat.get-color-from-palette($primary, 500);
10 | color: mat.get-contrast-color-from-palette($primary, 500);
11 | }
12 |
13 | app-card.app-color-accent mat-card {
14 | background-color: mat.get-color-from-palette($accent, 500);
15 | color: mat.get-contrast-color-from-palette($accent, 500);
16 | }
17 |
18 | app-card.app-color-warning mat-card {
19 | background-color: mat.get-color-from-palette($warning, 500);
20 | color: mat.get-contrast-color-from-palette($warning, 500);
21 | }
22 |
23 | app-card.app-color-success mat-card {
24 | /*Themes do not have a success map by default*/
25 | background-color: mat.get-color-from-palette($success, 500);
26 | color: mat.get-contrast-color-from-palette($success, 500);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/card/card.theme.scss:
--------------------------------------------------------------------------------
1 | @use "@angular/material" as mat;
2 | @mixin card-theme($theme) {
3 | $primary: map-get($theme, primary);
4 | $accent: map-get($theme, accent);
5 | $warning: map-get($theme, warn);
6 | $success: map-get($theme, success);
7 |
8 | app-card.app-color-primary mat-card {
9 | background-color: mat.get-color-from-palette($primary, 500);
10 | color: mat.get-contrast-color-from-palette($primary, 500);
11 | }
12 |
13 | app-card.app-color-accent mat-card {
14 | background-color: mat.get-color-from-palette($accent, 500);
15 | color: mat.get-contrast-color-from-palette($accent, 500);
16 | }
17 |
18 | app-card.app-color-warning mat-card {
19 | background-color: mat.get-color-from-palette($warning, 500);
20 | color: mat.get-contrast-color-from-palette($warning, 500);
21 | }
22 |
23 | app-card.app-color-success mat-card {
24 | /*Themes do not have a success map by default*/
25 | background-color: mat.get-color-from-palette($success, 500);
26 | color: mat.get-contrast-color-from-palette($success, 500);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/demo-app/ng15/README.md:
--------------------------------------------------------------------------------
1 | # DemoApp
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.4.0.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/demo-app/ng16/README.md:
--------------------------------------------------------------------------------
1 | # DemoApp
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.4.0.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of NgxFormErrors project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
4 |
5 | Communication through any of NgxFormErrors's channels (GitHub, Twitter ,Slack, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
6 |
7 | We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the NgxFormErrors project to do the same.
8 |
9 | If any member of the community violates this code of conduct, the maintainers of the NgxFormErrors project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
10 |
11 | If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [alexis.georges@nbb.be](mailto:alexis.georges@nbb.be).
12 |
--------------------------------------------------------------------------------
/scripts/helpers.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const fs = require("fs");
3 |
4 | /**
5 | * Helper functions.
6 | */
7 | const _root = path.resolve(process.cwd(), "."); // project root folder
8 |
9 | const currentFolder = path.basename(_root);
10 |
11 | const root = path.join.bind(path, _root);
12 |
13 | function getAngularCliAppConfig() {
14 | const applicationAngularCliConfigPath = root("angular.json");
15 |
16 | let angularCliConfigPath;
17 |
18 | if (fs.existsSync(applicationAngularCliConfigPath)) {
19 | angularCliConfigPath = applicationAngularCliConfigPath;
20 | } else {
21 | throw new Error("angular.json is not present. Please add this at the root your project because stark-build needs this.");
22 | }
23 |
24 | const angularCliConfig = require(angularCliConfigPath);
25 | if (angularCliConfig.defaultProject && angularCliConfig.projects[angularCliConfig.defaultProject]) {
26 | return angularCliConfig.projects[angularCliConfig.defaultProject];
27 | } else {
28 | throw new Error("Angular-cli config apps is wrong. Please adapt it to follow Angular CLI way.");
29 | }
30 | }
31 |
32 | module.exports = {
33 | currentFolder,
34 | getAngularCliAppConfig,
35 | root
36 | };
37 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/components/card/card.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, HostBinding, Input } from "@angular/core";
2 |
3 | @Component({
4 | selector: "app-card",
5 | templateUrl: "./card.component.html",
6 | styleUrls: ["./card.component.scss"]
7 | })
8 | export class CardComponent {
9 | @HostBinding("class.app-color-primary")
10 | public primaryColor = false;
11 | @HostBinding("class.app-color-accent")
12 | public accentColor = false;
13 | @HostBinding("class.app-color-warning")
14 | public warningColor = false;
15 | @HostBinding("class.app-color-success")
16 | public successColor = false;
17 |
18 | @Input()
19 | public set color(color: "primary" | "accent" | "warning" | "success") {
20 | this.primaryColor = false;
21 | this.accentColor = false;
22 | this.warningColor = false;
23 | this.successColor = false;
24 |
25 | switch (color) {
26 | case "primary":
27 | this.primaryColor = true;
28 | break;
29 | case "accent":
30 | this.accentColor = true;
31 | break;
32 | case "warning":
33 | this.warningColor = true;
34 | break;
35 | case "success":
36 | this.successColor = true;
37 | break;
38 | default:
39 | break;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/components/card/card.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, HostBinding, Input } from "@angular/core";
2 |
3 | @Component({
4 | selector: "app-card",
5 | templateUrl: "./card.component.html",
6 | styleUrls: ["./card.component.scss"]
7 | })
8 | export class CardComponent {
9 | @HostBinding("class.app-color-primary")
10 | public primaryColor = false;
11 | @HostBinding("class.app-color-accent")
12 | public accentColor = false;
13 | @HostBinding("class.app-color-warning")
14 | public warningColor = false;
15 | @HostBinding("class.app-color-success")
16 | public successColor = false;
17 |
18 | @Input()
19 | public set color(color: "primary" | "accent" | "warning" | "success") {
20 | this.primaryColor = false;
21 | this.accentColor = false;
22 | this.warningColor = false;
23 | this.successColor = false;
24 |
25 | switch (color) {
26 | case "primary":
27 | this.primaryColor = true;
28 | break;
29 | case "accent":
30 | this.accentColor = true;
31 | break;
32 | case "warning":
33 | this.warningColor = true;
34 | break;
35 | case "success":
36 | this.successColor = true;
37 | break;
38 | default:
39 | break;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
2 | import { AppComponent } from "./app.component";
3 | import { NO_ERRORS_SCHEMA } from "@angular/core";
4 | import { RouterTestingModule } from "@angular/router/testing";
5 |
6 | describe("AppComponent", () => {
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | return TestBed.configureTestingModule({
11 | imports: [RouterTestingModule],
12 | declarations: [AppComponent],
13 | schemas: [NO_ERRORS_SCHEMA]
14 | }).compileComponents();
15 | }));
16 |
17 | beforeEach(() => {
18 | fixture = TestBed.createComponent(AppComponent);
19 | fixture.detectChanges();
20 | });
21 |
22 | it("should create the app", () => {
23 | const app: AppComponent = fixture.debugElement.componentInstance;
24 | expect(app).toBeTruthy();
25 | });
26 |
27 | it("should render title in a h1 tag", () => {
28 | fixture.detectChanges();
29 | const compiled: HTMLElement = fixture.debugElement.nativeElement;
30 | const h1Element = compiled.querySelector("h1");
31 | expect(h1Element).toBeTruthy();
32 | // tslint:disable-next-line:no-non-null-assertion
33 | expect(h1Element!.textContent).toContain("Ngx-Form-Errors");
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
2 | import { AppComponent } from "./app.component";
3 | import { NO_ERRORS_SCHEMA } from "@angular/core";
4 | import { RouterTestingModule } from "@angular/router/testing";
5 |
6 | describe("AppComponent", () => {
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | return TestBed.configureTestingModule({
11 | imports: [RouterTestingModule],
12 | declarations: [AppComponent],
13 | schemas: [NO_ERRORS_SCHEMA]
14 | }).compileComponents();
15 | }));
16 |
17 | beforeEach(() => {
18 | fixture = TestBed.createComponent(AppComponent);
19 | fixture.detectChanges();
20 | });
21 |
22 | it("should create the app", () => {
23 | const app: AppComponent = fixture.debugElement.componentInstance;
24 | expect(app).toBeTruthy();
25 | });
26 |
27 | it("should render title in a h1 tag", () => {
28 | fixture.detectChanges();
29 | const compiled: HTMLElement = fixture.debugElement.nativeElement;
30 | const h1Element = compiled.querySelector("h1");
31 | expect(h1Element).toBeTruthy();
32 | // tslint:disable-next-line:no-non-null-assertion
33 | expect(h1Element!.textContent).toContain("Ngx-Form-Errors");
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## PR Checklist
2 |
3 | Please check if your PR fulfills the following requirements:
4 |
5 | - [ ] The commit message follows our guidelines: https://github.com/NationalBankBelgium/ngx-form-errors/blob/master/CONTRIBUTING.md#-commit-message-guidelines
6 | - [ ] Tests for the changes have been added (for bug fixes / features)
7 | - [ ] Docs have been added / updated (for bug fixes / features)
8 |
9 | ## PR Type
10 |
11 | What kind of change does this PR introduce?
12 |
13 |
14 |
15 | ```
16 | [ ] Bugfix
17 | [ ] Feature
18 | [ ] Code style update (formatting, local variables)
19 | [ ] Refactoring (no functional changes, no api changes)
20 | [ ] Build related changes
21 | [ ] CI related changes
22 | [ ] Documentation content changes
23 | [ ] Other... Please describe:
24 | ```
25 |
26 | ## What is the current behavior?
27 |
28 |
29 |
30 | Issue Number: N/A
31 |
32 | ## What is the new behavior?
33 |
34 | ## Does this PR introduce a breaking change?
35 |
36 | ```
37 | [ ] Yes
38 | [ ] No
39 | ```
40 |
41 |
42 |
43 | ## Other information
44 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "ngx-form-errors": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "library",
10 | "prefix": "ngx-form-errors",
11 | "architect": {
12 | "build": {
13 | "builder": "@angular-devkit/build-angular:ng-packagr",
14 | "options": {
15 | "tsConfig": "./tsconfig.lib.json",
16 | "project": "./ng-package.json"
17 | },
18 | "configurations": {
19 | "production": {
20 | "tsConfig": "tsconfig.lib.prod.json"
21 | }
22 | }
23 | },
24 | "test": {
25 | "builder": "@angular-devkit/build-angular:karma",
26 | "options": {
27 | "main": "./base.spec.ts",
28 | "tsConfig": "./tsconfig.spec.json",
29 | "karmaConfig": "./karma.conf.js"
30 | }
31 | },
32 | "lint": {
33 | "builder": "@angular-eslint/builder:lint",
34 | "options": {
35 | "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
36 | }
37 | }
38 | }
39 | }
40 | },
41 | "cli": {
42 | "schematicCollections": ["@angular-eslint/schematics"],
43 | "analytics": false
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/03-documentation.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '📄 Documentation'
3 | about: Documentation issue or request
4 | title: ''
5 | labels: 'documentation'
6 | ---
7 |
8 | # 📄 Documentation
9 |
10 | ## Is this a regression?
11 |
12 |
13 |
14 | [ ] No
15 | [ ] Yes, the issue was not present in version: ...
16 |
17 |
18 | ## Current behavior
19 |
20 |
21 |
22 | ## Expected behavior
23 |
24 |
25 |
26 | ## What is the motivation / use case for changing the behavior?
27 |
28 |
29 |
30 | ## Environment
31 |
32 |
33 | Angular version: X.Y.Z
34 | NgxFormErrors version: X.Y.Z
35 |
36 |
37 | Browser:
38 | - [ ] Chrome (desktop) version XX
39 | - [ ] Chrome (Android) version XX
40 | - [ ] Chrome (iOS) version XX
41 | - [ ] Firefox version XX
42 | - [ ] Safari (desktop) version XX
43 | - [ ] Safari (iOS) version XX
44 | - [ ] IE version XX
45 | - [ ] Edge version XX
46 |
47 | For Tooling issues:
48 | - Node version: XX
49 | - Platform:
50 |
51 | Others:
52 |
53 |
54 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/password-validator.ts:
--------------------------------------------------------------------------------
1 | import { AbstractControl, UntypedFormControl, UntypedFormGroup, ValidationErrors } from "@angular/forms";
2 | import { MatchingPassword } from "./pages";
3 |
4 | export class PasswordValidator {
5 | // Inspired on: http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview
6 | public static areEqual(formGroup: UntypedFormGroup): ValidationErrors | null {
7 | let value: string | undefined;
8 | let valid = true;
9 | for (const key in formGroup.controls) {
10 | /* eslint-disable-next-line no-prototype-builtins */
11 | if (formGroup.controls.hasOwnProperty(key)) {
12 | const control: UntypedFormControl = formGroup.controls[key];
13 |
14 | if (value === undefined) {
15 | value = control.value;
16 | } else {
17 | if (value !== control.value) {
18 | valid = false;
19 | break;
20 | }
21 | }
22 | }
23 | }
24 |
25 | if (valid) {
26 | /* eslint-disable-next-line no-null/no-null */
27 | return null;
28 | }
29 |
30 | return {
31 | areEqual: true
32 | };
33 | }
34 |
35 | public static areEqualTyped(formGroup: AbstractControl): ValidationErrors | null {
36 | const matchingPassword = formGroup.value;
37 |
38 | if (matchingPassword.password === matchingPassword.confirmPassword) {
39 | /* eslint-disable-next-line no-null/no-null */
40 | return null;
41 | }
42 | return {
43 | areEqual: true
44 | };
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/password-validator.ts:
--------------------------------------------------------------------------------
1 | import { AbstractControl, UntypedFormControl, UntypedFormGroup, ValidationErrors } from "@angular/forms";
2 | import { MatchingPassword } from "./pages";
3 |
4 | export class PasswordValidator {
5 | // Inspired on: http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview
6 | public static areEqual(formGroup: UntypedFormGroup): ValidationErrors | null {
7 | let value: string | undefined;
8 | let valid = true;
9 | for (const key in formGroup.controls) {
10 | /* eslint-disable-next-line no-prototype-builtins */
11 | if (formGroup.controls.hasOwnProperty(key)) {
12 | const control: UntypedFormControl = formGroup.controls[key];
13 |
14 | if (value === undefined) {
15 | value = control.value;
16 | } else {
17 | if (value !== control.value) {
18 | valid = false;
19 | break;
20 | }
21 | }
22 | }
23 | }
24 |
25 | if (valid) {
26 | /* eslint-disable-next-line no-null/no-null */
27 | return null;
28 | }
29 |
30 | return {
31 | areEqual: true
32 | };
33 | }
34 |
35 | public static areEqualTyped(formGroup: AbstractControl): ValidationErrors | null {
36 | const matchingPassword = formGroup.value;
37 |
38 | if (matchingPassword.password === matchingPassword.confirmPassword) {
39 | /* eslint-disable-next-line no-null/no-null */
40 | return null;
41 | }
42 | return {
43 | areEqual: true
44 | };
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/01-bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '🐛 Bug Report'
3 | about: Report a bug or regression
4 | title: ''
5 | labels: 'bug'
6 | ---
7 |
8 | # 🐛 Bug Report
9 |
10 | ## Is this a regression?
11 |
12 |
13 |
14 | [ ] No
15 | [ ] Yes, the bug was not present in version: ...
16 |
17 |
18 | ## Current behavior
19 |
20 |
21 |
22 | ## Expected behavior
23 |
24 |
25 |
26 | ## 🔬 Minimal reproduction of the problem with instructions
27 |
28 |
32 |
33 | ## Environment
34 |
35 |
36 | Angular version: X.Y.Z
37 | NgxFormErrors version: X.Y.Z
38 |
39 |
40 | Browser:
41 | - [ ] Chrome (desktop) version XX
42 | - [ ] Chrome (Android) version XX
43 | - [ ] Chrome (iOS) version XX
44 | - [ ] Firefox version XX
45 | - [ ] Safari (desktop) version XX
46 | - [ ] Safari (iOS) version XX
47 | - [ ] IE version XX
48 | - [ ] Edge version XX
49 |
50 | For Tooling issues:
51 | - Node version: XX
52 | - Platform:
53 |
54 | Others:
55 |
56 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/ngx-forms-example/ngx-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/ngx-forms-example/ngx-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/reactive-forms-example/reactive-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/reactive-forms-example/reactive-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/demo-app/ng15/src/app/pages/typed-reactive-forms-example/typed-reactive-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/demo-app/ng16/src/app/pages/typed-reactive-forms-example/typed-reactive-forms-example.component.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 |
3 | .grid {
4 | display: grid;
5 | grid-template-rows: auto;
6 | grid-template-columns: 1fr 1fr 1fr;
7 | grid-gap: 10px;
8 | }
9 |
10 | .form-card {
11 | grid-column-start: 1;
12 | grid-column-end: 4;
13 |
14 | mat-form-field {
15 | box-sizing: border-box;
16 | width: 100%;
17 |
18 | @media #{$monitor-query} {
19 | width: 45%;
20 | &:last-child {
21 | margin-left: 10%;
22 | }
23 | }
24 | }
25 |
26 | mat-card-actions {
27 | display: flex;
28 | align-items: flex-start;
29 | flex-wrap: wrap;
30 | margin: -10px;
31 |
32 | button {
33 | margin: 10px;
34 | @media #{$mobile-query} {
35 | width: 100%;
36 | }
37 | }
38 | }
39 | }
40 |
41 | .form-field-info {
42 | @media #{$table-query} {
43 | grid-column-start: 1;
44 | grid-column-end: 4;
45 | }
46 |
47 | mat-card-content {
48 | > div {
49 | padding: 5px 0;
50 | }
51 |
52 | pre {
53 | overflow: auto;
54 | box-sizing: border-box;
55 | display: block;
56 | width: 100%;
57 | max-height: 75px;
58 |
59 | margin: inherit;
60 | padding: 5px;
61 | border-radius: 4px;
62 |
63 | background-color: rgba(0, 0, 0, 0.2);
64 |
65 | &:empty {
66 | display: none;
67 | }
68 | }
69 | }
70 | }
71 |
72 | .form-validation-messages {
73 | grid-column-start: 1;
74 | grid-column-end: 4;
75 | }
76 |
--------------------------------------------------------------------------------
/src/directives/form-errors-group.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, Input, OnInit } from "@angular/core";
2 |
3 | /**
4 | * Directive that defines the group of the form model to be validated.
5 | * The directive exposes the group through the controller to allow access to it by wrapped {@link NgxFormErrorsDirective}(s).
6 | */
7 | @Directive({
8 | selector: "[ngxFormErrorsGroup]"
9 | })
10 | export class NgxFormErrorsGroupDirective implements OnInit {
11 | /**
12 | * The group of the form model
13 | */
14 | @Input("ngxFormErrorsGroup")
15 | public set group(value: string) {
16 | this._formErrorsGroup = value;
17 | }
18 |
19 | public get group(): string {
20 | return this._formErrorsGroup;
21 | }
22 |
23 | /**
24 | * @ignore
25 | */
26 | private _formErrorsGroup!: string;
27 |
28 | /**
29 | * Class constructor
30 | */
31 | // eslint-disable-next-line @typescript-eslint/no-useless-constructor
32 | public constructor() {
33 | // TODO: how to prevent multiple ngxFormErrorsGroup on the same