diff --git a/README.md b/README.md index fb3f163..6ac3f28 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,8 @@ Let's just have a look at the following example: -
- Field is required -
-
- Min length is 5 -
+
Field is required
+
Min length is 5
Field must contain at least one uppercase, one lowercase, and one number
@@ -61,14 +57,10 @@ You decide how to display the messages by defining your own Error component: -
- {{ error.message }} -
+
{{ error.message }}
-
- {{ constructDisplayedErrorMMessage(error) }} -
+
{{ constructDisplayedErrorMMessage(error) }}
``` And the messages are centralized in a service: diff --git a/demo-app/package-lock.json b/demo-app/package-lock.json index e799ddb..5af47a4 100644 --- a/demo-app/package-lock.json +++ b/demo-app/package-lock.json @@ -156,9 +156,9 @@ } }, "@angular/animations": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.0.tgz", - "integrity": "sha512-xi832o3YN+eYSV4PDRllc8JwkH4aKPlb7NZ0UaqchOmz9/jQcykCEMZDzQAZUgHG1ohay6JBVaV8/zNcbSsRCA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.1.tgz", + "integrity": "sha512-2AHc4HYz2cUVW3E0oYOTyUzBTnPJdtmVOx/Uo6+jnRqikvOGFOc5VXzFIYODe1Iiy+EYcSZ1lvQqwUbpZd6gwA==", "requires": { "tslib": "^1.9.0" } @@ -190,25 +190,25 @@ } }, "@angular/common": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.0.tgz", - "integrity": "sha512-5HNGT+XsY+7sQcNoFRqhbUfVdnBAtXaupmMbBclnQHTon9y9Ijp0ocYi7zxx39feo6xYF5HhBMnDPkFgtAnsYQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.1.tgz", + "integrity": "sha512-lYf3MeVMz69EriS5ANFY5PerJK0i4xHp/Jy67reb8ydZ+sfW320PUMuFtx3bZvk9PD7NdL3QZvXmla/ogrltTQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.0.tgz", - "integrity": "sha512-On1qj4yQoIGxGOQ09ohTq0QNjrIJtWcwnCXYAEEyc83eadBMOqiFh6SUMgX1O+B7BIB4mebFw/n/etez0A21xw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.1.tgz", + "integrity": "sha512-wf9w882hNoRaTDRqkEvQxV7nGB3liTX/LWEMunmm/Yz0nWkvgErR9pIHv3Sm4Ox0hyG3GdMpcVBzQ8qPomGOag==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler-cli": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.0.tgz", - "integrity": "sha512-BKELLAnA4jWfyPEzuVxTNNFAPKJOyh4Xjw7c+dRf90bnw9iIgZOpz9WXSN/xfEhftqRPTnPcfs56i6bxqeYCCQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.1.tgz", + "integrity": "sha512-ImmKTnBbAWIY7qrYSPFLJE83VYcDX7zK6Ig/vOl4e6dzvpZfJDYHMT8ELeWj7a2nkL9SjT8X3o9Mkbrb75Tepg==", "dev": true, "requires": { "canonical-path": "1.0.0", @@ -543,25 +543,25 @@ } }, "@angular/core": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.0.tgz", - "integrity": "sha512-tlCDDM9IknXvVLk1sg0lzCO4OREM54i1bFtTpl5kPtugK6l4kYCOH78UzDPHnOzzI3LGLj8Hb2NiObVa9c4fdg==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.1.tgz", + "integrity": "sha512-FYNAf4chxBoIVGCW2+fwR2MB2Ur5v1aG9L6zCcMXlZLbR64bu5j2m4e70RhXk/VptKvYWJ45od3xE5KfcaeEig==", "requires": { "tslib": "^1.9.0" } }, "@angular/forms": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.0.tgz", - "integrity": "sha512-vgnKgThitbaSQekTFt8qCFejnBwBMNJDUm7LJFcvRn4wcZKArTARTfSKHudNYCjTEqs9/YT4TJQTm9flVRbUJw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.1.tgz", + "integrity": "sha512-MxinNUvl02UfpY9gJtbTU6Mdt9fjIJYOGskcpwm+5u9jiMeRvOnG94ySoNrygg3EWwScX+P0mM4KN6fJWau7gQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/language-service": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.0.tgz", - "integrity": "sha512-UDmIRR0ybdafrJLHkSDgc/3PpsA9lnwXqGMSAyyhEF3InORFFkloAb0a6Naz+y8ePgEMfqbpyWjtzo8qGtOmEQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.1.tgz", + "integrity": "sha512-LmeoO7KXBcPRDvQpBv+ttG9oalCE0z7+AxbJBJNrrwzypP624B3xX2XWai9XUNkxu+shqE00lAU2lC7Fxs5v7A==", "dev": true }, "@angular/material": { @@ -573,25 +573,25 @@ } }, "@angular/platform-browser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.0.tgz", - "integrity": "sha512-ClrGYlacK0kexE7eHLfruOjgJl0MtMt7RsMv5i757GUwbOm1dCwG1HK8cLNDZJFHMZodKVKwEGS6/R5Cl6vrNg==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.1.tgz", + "integrity": "sha512-/6uHdFLmRkrkeOo+TzScrLG2YydG8kBNyT6ZpSOBf+bmB5DHyIGd55gh/tQJteKrnyadxRhqWCLBTYAbVX9Pnw==", "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.0.tgz", - "integrity": "sha512-IiyBcQIQVDZMxfpTYex1QfPmcMubKLgu1pCvQsjr0HmUEySqcykO+FzHlYLf5TTgRrtkI6cP2pYzTHGVR93Gpg==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.1.tgz", + "integrity": "sha512-hrSkI7aESEkqYnu628Z/LvYNlUNMqIqkXYAkT3urxFdCw7UwNeZKrDmd9sRwK3gK3sC1VeD9pXtqaKmGsnBjOA==", "requires": { "tslib": "^1.9.0" } }, "@angular/router": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.0.tgz", - "integrity": "sha512-Jpm0Y5IH30hIQsbnLgi2/LgHbArfE9gWMj/9mDIUOlJeQfGzNVoifBE+zLLJU/wb09+ZtfwGBxkMeDTitH/n2A==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.1.tgz", + "integrity": "sha512-3qMZnhFr6xx3dMy14rKwIw9ISTOZlsp9jAkthXVsfA2/thffScXHPBrH4SipkySLmOAtPmF5m5jscy8mx/1mJQ==", "requires": { "tslib": "^1.9.0" } diff --git a/demo-app/src/app/app-routing.module.ts b/demo-app/src/app/app-routing.module.ts index 35e6adf..1e8a3b3 100644 --- a/demo-app/src/app/app-routing.module.ts +++ b/demo-app/src/app/app-routing.module.ts @@ -1,13 +1,14 @@ +/*tslint:disable:completed-docs */ import { NgModule } from "@angular/core"; import { Routes, RouterModule } from "@angular/router"; -//import {AppComponent} from "./app.component"; import { ReactiveFormsExampleComponent } from "./pages/reactive-forms-example/reactive-forms-example.component"; import { NgxFormsExampleComponent } from "./pages/ngx-forms-example/ngx-forms-example.component"; +import { TemplateDrivenFormsExampleComponent } from "./pages/template-driven-forms-example/template-driven-forms-example.component"; const routes: Routes = [ - //{ path: "", component: AppComponent}, - { path: "", redirectTo: "/reactive-forms", pathMatch: "full" }, + { path: "", redirectTo: "/template-driven-forms", pathMatch: "full" }, { path: "reactive-forms", component: ReactiveFormsExampleComponent }, + { path: "template-driven-forms", component: TemplateDrivenFormsExampleComponent }, { path: "ngx-form-errors", component: NgxFormsExampleComponent } ]; diff --git a/demo-app/src/app/app.component.html b/demo-app/src/app/app.component.html index 8d37d70..7fa1d30 100644 --- a/demo-app/src/app/app.component.html +++ b/demo-app/src/app/app.component.html @@ -1,10 +1,13 @@

Ngx-Form-Errors

Validation messages in Reactive Forms made easy

+ + - + + Template Driven Forms Reactive Forms Ngx Form Errors diff --git a/demo-app/src/app/app.component.scss b/demo-app/src/app/app.component.scss index 11610e0..8a95ffe 100644 --- a/demo-app/src/app/app.component.scss +++ b/demo-app/src/app/app.component.scss @@ -1,3 +1,4 @@ +/* .form-group { display: flex; align-items: start; @@ -49,3 +50,4 @@ border: 1px #000 solid; border-radius: 6px; } +*/ diff --git a/demo-app/src/app/app.component.ts b/demo-app/src/app/app.component.ts index 639255e..900bb8b 100644 --- a/demo-app/src/app/app.component.ts +++ b/demo-app/src/app/app.component.ts @@ -7,7 +7,11 @@ import { Component, OnInit } from "@angular/core"; styleUrls: ["./app.component.scss"] }) export class AppComponent implements OnInit { - public constructor() {} + public constructor() { + /*empty*/ + } - public ngOnInit(): void {} + public ngOnInit(): void { + /*empty*/ + } } diff --git a/demo-app/src/app/app.module.ts b/demo-app/src/app/app.module.ts index d908afb..45600dc 100644 --- a/demo-app/src/app/app.module.ts +++ b/demo-app/src/app/app.module.ts @@ -17,6 +17,7 @@ import { LanguageSelectorComponent, SimpleFormErrorComponent, TranslatedFormErro import { AppRoutingModule } from "./app-routing.module"; import { ReactiveFormsExampleComponent } from "./pages/reactive-forms-example/reactive-forms-example.component"; import { NgxFormsExampleComponent } from "./pages/ngx-forms-example/ngx-forms-example.component"; +import { TemplateDrivenFormsExampleComponent } from "./pages/template-driven-forms-example/template-driven-forms-example.component"; /* tslint:disable:no-hardcoded-credentials */ @NgModule({ @@ -26,7 +27,8 @@ import { NgxFormsExampleComponent } from "./pages/ngx-forms-example/ngx-forms-ex SimpleFormErrorComponent, TranslatedFormErrorComponent, ReactiveFormsExampleComponent, - NgxFormsExampleComponent + NgxFormsExampleComponent, + TemplateDrivenFormsExampleComponent ], imports: [ BrowserModule, diff --git a/demo-app/src/app/components/translated-form-error/translated-form-error.component.html b/demo-app/src/app/components/translated-form-error/translated-form-error.component.html index cd24e08..00c3999 100644 --- a/demo-app/src/app/components/translated-form-error/translated-form-error.component.html +++ b/demo-app/src/app/components/translated-form-error/translated-form-error.component.html @@ -1 +1 @@ -
{{ error.message | translate: error.params }}
+
{{ error.message | translate: error.params }}
diff --git a/demo-app/src/app/components/translated-form-error/translated-form-error.component.ts b/demo-app/src/app/components/translated-form-error/translated-form-error.component.ts index 5b57b45..2574e6e 100644 --- a/demo-app/src/app/components/translated-form-error/translated-form-error.component.ts +++ b/demo-app/src/app/components/translated-form-error/translated-form-error.component.ts @@ -25,19 +25,18 @@ export class TranslatedFormErrorComponent implements NgxFormErrorComponent, OnIn public checkForErrors(): void { this.errors$.subscribe((errors: NgxFormFieldError[]) => { - // the formField can be retrieved from the "fieldName" param of any of the errors + this.errors = errors; + if (errors.length) { + // the formField can be retrieved from the "fieldName" param of any of the errors this.fieldName = errors[0].params.fieldName; + this.translateFieldName(); } - - this.errors = errors; - this.translateFieldName(); }); } public translateFieldName(): void { for (const error of this.errors) { - console.log("-------- translating error.params.fieldName", this.fieldName); error.params = { ...error.params, fieldName: this.translateService.instant(this.fieldName) }; } } diff --git a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.html b/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.html index 3b810f1..d69acbf 100644 --- a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.html +++ b/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.html @@ -31,43 +31,63 @@
+ +
-
+
Has errors: {{ usernameField.hasErrors }}
Has 'required' error: {{ usernameField.hasError("required") }}
-
Is valid?: {{ usernameField.isValid() }}
-
'required' error: {{ usernameField.getError("required") | json }}
-
Is touched? {{ usernameField.hasState("touched") }}
-
Errors: {{ usernameField.errors | json }}
+
Is valid?: {{ usernameField.isValid() }}
Is touched? {{ usernameField.hasState("touched") }}
+
+ 'required' error:
{{ usernameField.getError("required") | json }}
+
+
+ Errors:
{{ usernameField.errors | json }}
+
Has errors: {{ passwordField.hasErrors }}
Has 'pattern' error: {{ passwordField.hasError("pattern") }}
-
Is valid?: {{ passwordField.isValid() }}
Is touched? {{ passwordField.hasState("touched") }}
-
'pattern' error: {{ passwordField.getError("pattern") | json }}
-
Errors: {{ passwordField.errors | json }}
+
Is pattern valid?: {{ passwordField.isValid("pattern") }}
+
Is touched? {{ passwordField.hasState("touched") }}
+
+ 'pattern' error:
{{ passwordField.getError("pattern") | json }}
+
+
+ Errors:
{{ passwordField.errors | json }}
+
Has errors: {{ confirmPasswordField.hasErrors }}
-
Has 'pattern' error: {{ confirmPasswordField.hasError("pattern") }}
-
Is valid?: {{ confirmPasswordField.isValid() }}
+
Has 'required' error: {{ confirmPasswordField.hasError("required") }}
+
Is required valid?: {{ confirmPasswordField.isValid("required") }}
Is touched? {{ confirmPasswordField.hasState("touched") }}
-
'pattern' error: {{ confirmPasswordField.getError("pattern") | json }}
-
Errors: {{ confirmPasswordField.errors | json }}
+
+ 'required' error:
{{ confirmPasswordField.getError("required") | json }}
+
+
+ Errors:
{{ confirmPasswordField.errors | json }}
+
- + + No validation errors + +
+ + + +
+
diff --git a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.scss b/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.scss index 2f80701..8cf356b 100644 --- a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.scss +++ b/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.scss @@ -1,6 +1,6 @@ .form-group { display: flex; - align-items: start; + align-items: flex-start; flex-direction: column; flex-wrap: wrap; @@ -18,12 +18,13 @@ .form-buttons { margin-top: 25px; - button:first-child { - margin-right: 20px; - } button { + margin-left: 20px; min-width: 150px; } + button:first-child { + margin-left: 0; + } } } @@ -33,19 +34,19 @@ flex-wrap: wrap; justify-content: flex-start; - /*& .form-field-info { - background-color: aliceblue; - border: 1px #000 solid; - border-radius: 6px; - width: 350px; - margin: 20px; - margin-right: 20px; - }*/ + & .form-field-info { + background-color: #94c6ff; + width: 350px; + margin: 20px; + margin-right: 20px; + } } .form-validation-messages { margin: 20px; - background-color: cornsilk; - border: 1px #000 solid; - border-radius: 6px; + background-color: #ffb006; + + &.no-errors { + background-color: #3fbf35; + } } diff --git a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.spec.ts b/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.spec.ts deleted file mode 100644 index 96e752d..0000000 --- a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { async, ComponentFixture, TestBed } from "@angular/core/testing"; - -import { NgxFormsExampleComponent } from "./ngx-forms-example.component"; - -describe("NgxFormsExampleComponent", () => { - let component: NgxFormsExampleComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [NgxFormsExampleComponent] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(NgxFormsExampleComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.ts b/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.ts index 848b59e..9ba1045 100644 --- a/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.ts +++ b/demo-app/src/app/pages/ngx-forms-example/ngx-forms-example.component.ts @@ -1,8 +1,9 @@ +/*tslint:disable:completed-docs trackBy-function template-cyclomatic-complexity*/ import { Component, OnInit } from "@angular/core"; import { FormGroup, Validators, FormControl, FormBuilder, AbstractControl } from "@angular/forms"; import { ErrorStateMatcher } from "@angular/material/core"; import { ParentErrorStateMatcher } from "../../parent-error-state-matcher"; -import { PasswordValidator } from "src/app/password-validator"; +import { PasswordValidator } from "../../password-validator"; @Component({ selector: "app-ngx-forms-example", @@ -14,10 +15,12 @@ export class NgxFormsExampleComponent implements OnInit { public errors: object; public parentErrorStateMatcher: ErrorStateMatcher = new ParentErrorStateMatcher(); public passwordPattern: string = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$"; + public showValidationDetails: boolean = true; + public showValidationSummary: boolean = true; public constructor(private formBuilder: FormBuilder) {} - public ngOnInit() { + public ngOnInit(): void { this.formNgxError = this.formBuilder.group({ username: [undefined, Validators.required], matchingPasswords: new FormGroup( @@ -42,6 +45,14 @@ export class NgxFormsExampleComponent implements OnInit { }); } + public toggleValidationDetails(): void { + this.showValidationDetails = !this.showValidationDetails; + } + + public toggleValidationSummary(): void { + this.showValidationSummary = !this.showValidationSummary; + } + public onSubmitUserDetails(formGroup: FormGroup): void { console.log("CCR==========> onSubmitUserDetails value", formGroup.value); } diff --git a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.html b/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.html index 7866a67..70b7b59 100644 --- a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.html +++ b/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.html @@ -1,5 +1,5 @@
-
Plain Reactive Forms
+
Reactive Forms (Model Driven Forms)
@@ -65,32 +65,110 @@
+ +
-
+
- - - - - - +
Has errors: {{ !!formMatError.get("username").errors }}
+
Has 'required' error: {{ formMatError.get("username").hasError("required") }}
+ +
+ 'required' error:
{{ formMatError.get("username").getError("required") | json }}
+
+
Is touched? {{ formMatError.get("username").touched }}
+
+ Errors:
{{ formMatError.get("username").errors | json }}
+
- - - - - - +
Has errors: {{ !!formMatError.get("matchingPasswords.password").errors }}
+
Has 'pattern' error: {{ formMatError.get("matchingPasswords.password").hasError("pattern") }}
+ +
Is touched? {{ formMatError.get("matchingPasswords.password").touched }}
+
+ 'pattern' error:
{{ formMatError.get("matchingPasswords.password").getError("pattern") | json }}
+
+
+ Errors:
{{ formMatError.get("matchingPasswords.password").errors | json }}
+
- - - - - - +
Has errors: {{ !!formMatError.get("matchingPasswords.confirmPassword").errors }}
+
Has 'required' error: {{ formMatError.get("matchingPasswords.confirmPassword").hasError("required") }}
+ +
Is touched? {{ formMatError.get("matchingPasswords.confirmPassword").touched }}
+
+ 'required' error:
{{ formMatError.get("matchingPasswords.confirmPassword").getError("required") | json }}
+
+
+ Errors:
{{ formMatError.get("matchingPasswords.confirmPassword").errors | json }}
+
+ + +

+ No validation errors +

+
    + + +
  • + {{ validation.message | translate }} +
  • +
    +
    + + +
  • + {{ validation.message | translate }} +
  • +
    +
    + + +
  • + {{ validation.message | translate }} +
  • +
    +
    +
+
diff --git a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.scss b/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.scss index 11610e0..8cf356b 100644 --- a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.scss +++ b/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.scss @@ -1,6 +1,6 @@ .form-group { display: flex; - align-items: start; + align-items: flex-start; flex-direction: column; flex-wrap: wrap; @@ -18,12 +18,13 @@ .form-buttons { margin-top: 25px; - button:first-child { - margin-right: 20px; - } button { + margin-left: 20px; min-width: 150px; } + button:first-child { + margin-left: 0; + } } } @@ -34,9 +35,7 @@ justify-content: flex-start; & .form-field-info { - background-color: aliceblue; - border: 1px #000 solid; - border-radius: 6px; + background-color: #94c6ff; width: 350px; margin: 20px; margin-right: 20px; @@ -45,7 +44,9 @@ .form-validation-messages { margin: 20px; - background-color: cornsilk; - border: 1px #000 solid; - border-radius: 6px; + background-color: #ffb006; + + &.no-errors { + background-color: #3fbf35; + } } diff --git a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.spec.ts b/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.spec.ts deleted file mode 100644 index 23e5388..0000000 --- a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { async, ComponentFixture, TestBed } from "@angular/core/testing"; - -import { ReactiveFormsExampleComponent } from "./reactive-forms-example.component"; - -describe("ReactiveFormsExampleComponent", () => { - let component: ReactiveFormsExampleComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ReactiveFormsExampleComponent] - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ReactiveFormsExampleComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.ts b/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.ts index e7eb0ed..c33d938 100644 --- a/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.ts +++ b/demo-app/src/app/pages/reactive-forms-example/reactive-forms-example.component.ts @@ -1,6 +1,9 @@ +/*tslint:disable:completed-docs trackBy-function template-cyclomatic-complexity*/ import { Component, OnInit } from "@angular/core"; import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { ErrorStateMatcher } from "@angular/material/core"; import { PasswordValidator } from "../../password-validator"; +import { ParentErrorStateMatcher } from "../../parent-error-state-matcher"; @Component({ selector: "app-reactive-forms-example", @@ -10,11 +13,10 @@ import { PasswordValidator } from "../../password-validator"; export class ReactiveFormsExampleComponent implements OnInit { public formMatError: FormGroup; public validationMessages: object; + public parentErrorStateMatcher: ErrorStateMatcher = new ParentErrorStateMatcher(); public passwordPattern: string = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$"; - - public get reactiveForm(): FormGroup { - return this.formMatError; - } + public showValidationDetails: boolean = true; + public showValidationSummary: boolean = true; public constructor(private formBuilder: FormBuilder) {} @@ -49,15 +51,6 @@ export class ReactiveFormsExampleComponent implements OnInit { type: "required", message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.USER_NAME.REQUIRED" }, - { - type: "minlength", - message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.USER_NAME.MIN_LENGTH" - }, - { - type: "maxlength", - message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.USER_NAME.MAX_LENGTH" - }, - { type: "pattern", message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.USER_NAME.PATTERN" }, { type: "unique", message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.USER_NAME.UNIQUE" } ], password: [ @@ -84,6 +77,14 @@ export class ReactiveFormsExampleComponent implements OnInit { }; } + public toggleValidationDetails(): void { + this.showValidationDetails = !this.showValidationDetails; + } + + public toggleValidationSummary(): void { + this.showValidationSummary = !this.showValidationSummary; + } + public onSubmitUserDetails(formGroup: FormGroup): void { console.log("CCR==========> onSubmitUserDetails value", formGroup.value); } diff --git a/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.html b/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.html new file mode 100644 index 0000000..d18821d --- /dev/null +++ b/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.html @@ -0,0 +1,142 @@ +
+
Template Driven Forms
+ + + + +
+ {{ validation.message | translate }} +
+
+
+
+ + + +
+ {{ validation.message | translate }} +
+
+
+ + + + +
+ {{ validation.message | translate }} +
+
+
+
+
+ + + + +
+ +
+ +
Has errors: {{ !!usernameField.errors }}
Has 'required' error: {{ usernameField.hasError("required") }}
+ +
+ 'required' error:
{{ usernameField.getError("required") | json }}
+
+
Is touched? {{ usernameField.touched }}
+
+ Errors:
{{ usernameField.errors | json }}
+
+
+ +
Has errors: {{ !!passwordField.errors }}
Has 'pattern' error: {{ passwordField.hasError("pattern") }}
+ +
Is touched? {{ passwordField.touched }}
+
+ 'pattern' error:
{{ passwordField.getError("pattern") | json }}
+
+
+ Errors:
{{ passwordField.errors | json }}
+
+
+ +
Has errors: {{ !!confirmPasswordField.errors }}
+
Has 'required' error: {{ confirmPasswordField.hasError("required") }}
+ +
Is touched? {{ confirmPasswordField.touched }}
+
+ 'required' error:
{{ confirmPasswordField.getError("required") | json }}
+
+
+ Errors:
{{ confirmPasswordField.errors | json }}
+
+
+
+ + +

No validation errors

+
    + +
  • {{ validation.message | translate }}
  • +
    + + +
  • {{ validation.message | translate }}
  • +
    + +
  • + {{ validation.message | translate }} +
  • +
    +
+
+
diff --git a/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.scss b/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.scss new file mode 100644 index 0000000..8cf356b --- /dev/null +++ b/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.scss @@ -0,0 +1,52 @@ +.form-group { + display: flex; + align-items: flex-start; + flex-direction: column; + flex-wrap: wrap; + + .matching-passwords { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + + mat-form-field { + width: 45%; + } + } + + .form-buttons { + margin-top: 25px; + + button { + margin-left: 20px; + min-width: 150px; + } + button:first-child { + margin-left: 0; + } + } +} + +.form-validation-info { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + + & .form-field-info { + background-color: #94c6ff; + width: 350px; + margin: 20px; + margin-right: 20px; + } +} + +.form-validation-messages { + margin: 20px; + background-color: #ffb006; + + &.no-errors { + background-color: #3fbf35; + } +} diff --git a/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.ts b/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.ts new file mode 100644 index 0000000..80cd6a4 --- /dev/null +++ b/demo-app/src/app/pages/template-driven-forms-example/template-driven-forms-example.component.ts @@ -0,0 +1,74 @@ +/*tslint:disable:completed-docs trackBy-function template-cyclomatic-complexity*/ +import { Component, OnInit } from "@angular/core"; +import { NgForm } from "@angular/forms"; +import { ErrorStateMatcher } from "@angular/material/core"; +import { ParentErrorStateMatcher } from "../../parent-error-state-matcher"; + +@Component({ + selector: "app-template-driven-forms-example", + templateUrl: "./template-driven-forms-example.component.html", + styleUrls: ["./template-driven-forms-example.component.scss"] +}) +export class TemplateDrivenFormsExampleComponent implements OnInit { + public username: string; + public password: string; + public confirmPassword: string; + + public parentErrorStateMatcher: ErrorStateMatcher = new ParentErrorStateMatcher(); + public passwordPattern: string = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$"; + public validationMessages: object; + public showValidationDetails: boolean = true; + public showValidationSummary: boolean = true; + + public constructor() { + /*empty*/ + } + + public ngOnInit(): void { + this.validationMessages = { + username: [ + { + type: "required", + message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.USER_NAME.REQUIRED" + } + ], + password: [ + { + type: "required", + message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.PASSWORD.REQUIRED" + }, + { + type: "minlength", + message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.PASSWORD.MIN_LENGTH" + }, + { type: "pattern", message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.PASSWORD.PATTERN" } + ], + confirmPassword: [ + { + type: "required", + message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.CONFIRM_PASSWORD.REQUIRED" + }, + { + type: "areEqual", + message: "DEMO.FORM_VALIDATION.WITHOUT_NGX_FORM_ERRORS.CONFIRM_PASSWORD.ARE_EQUAL" + } + ] + }; + } + + public toggleValidationDetails(): void { + this.showValidationDetails = !this.showValidationDetails; + } + + public toggleValidationSummary(): void { + this.showValidationSummary = !this.showValidationSummary; + } + + public onSubmitUserDetails(formGroup: NgForm): void { + console.log("CCR==========> onSubmitUserDetails value", formGroup.value); + } + + public getFormStatus(formGroup: NgForm): void { + console.log("CCR==========> form status", formGroup); + } +} diff --git a/package-lock.json b/package-lock.json index 40db34a..5b17c14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,27 +55,27 @@ } }, "@angular/common": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.0.tgz", - "integrity": "sha512-5HNGT+XsY+7sQcNoFRqhbUfVdnBAtXaupmMbBclnQHTon9y9Ijp0ocYi7zxx39feo6xYF5HhBMnDPkFgtAnsYQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.1.tgz", + "integrity": "sha512-lYf3MeVMz69EriS5ANFY5PerJK0i4xHp/Jy67reb8ydZ+sfW320PUMuFtx3bZvk9PD7NdL3QZvXmla/ogrltTQ==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "@angular/compiler": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.0.tgz", - "integrity": "sha512-On1qj4yQoIGxGOQ09ohTq0QNjrIJtWcwnCXYAEEyc83eadBMOqiFh6SUMgX1O+B7BIB4mebFw/n/etez0A21xw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.1.tgz", + "integrity": "sha512-wf9w882hNoRaTDRqkEvQxV7nGB3liTX/LWEMunmm/Yz0nWkvgErR9pIHv3Sm4Ox0hyG3GdMpcVBzQ8qPomGOag==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "@angular/compiler-cli": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.0.tgz", - "integrity": "sha512-BKELLAnA4jWfyPEzuVxTNNFAPKJOyh4Xjw7c+dRf90bnw9iIgZOpz9WXSN/xfEhftqRPTnPcfs56i6bxqeYCCQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.1.tgz", + "integrity": "sha512-ImmKTnBbAWIY7qrYSPFLJE83VYcDX7zK6Ig/vOl4e6dzvpZfJDYHMT8ELeWj7a2nkL9SjT8X3o9Mkbrb75Tepg==", "dev": true, "requires": { "canonical-path": "1.0.0", @@ -231,36 +231,36 @@ } }, "@angular/core": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.0.tgz", - "integrity": "sha512-tlCDDM9IknXvVLk1sg0lzCO4OREM54i1bFtTpl5kPtugK6l4kYCOH78UzDPHnOzzI3LGLj8Hb2NiObVa9c4fdg==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.1.tgz", + "integrity": "sha512-FYNAf4chxBoIVGCW2+fwR2MB2Ur5v1aG9L6zCcMXlZLbR64bu5j2m4e70RhXk/VptKvYWJ45od3xE5KfcaeEig==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "@angular/forms": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.0.tgz", - "integrity": "sha512-vgnKgThitbaSQekTFt8qCFejnBwBMNJDUm7LJFcvRn4wcZKArTARTfSKHudNYCjTEqs9/YT4TJQTm9flVRbUJw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.1.tgz", + "integrity": "sha512-MxinNUvl02UfpY9gJtbTU6Mdt9fjIJYOGskcpwm+5u9jiMeRvOnG94ySoNrygg3EWwScX+P0mM4KN6fJWau7gQ==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.0.tgz", - "integrity": "sha512-ClrGYlacK0kexE7eHLfruOjgJl0MtMt7RsMv5i757GUwbOm1dCwG1HK8cLNDZJFHMZodKVKwEGS6/R5Cl6vrNg==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.1.tgz", + "integrity": "sha512-/6uHdFLmRkrkeOo+TzScrLG2YydG8kBNyT6ZpSOBf+bmB5DHyIGd55gh/tQJteKrnyadxRhqWCLBTYAbVX9Pnw==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.0.tgz", - "integrity": "sha512-IiyBcQIQVDZMxfpTYex1QfPmcMubKLgu1pCvQsjr0HmUEySqcykO+FzHlYLf5TTgRrtkI6cP2pYzTHGVR93Gpg==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.1.tgz", + "integrity": "sha512-hrSkI7aESEkqYnu628Z/LvYNlUNMqIqkXYAkT3urxFdCw7UwNeZKrDmd9sRwK3gK3sC1VeD9pXtqaKmGsnBjOA==", "dev": true, "requires": { "tslib": "^1.9.0"