Skip to content

Commit

Permalink
feat(): add if-validator (#40)
Browse files Browse the repository at this point in the history
* feat(): add if-validator

* feat(): update if validator project.json

Co-authored-by: Chau Tran <[email protected]>

* feat(): update if validator document

Co-authored-by: Chau Tran <[email protected]>

* feat(): add e2e testing for if validator

* Update apps/test-app/src/app/if-validator/if-validator.component.ts

* Update apps/test-app/src/app/if-validator/if-validator.component.ts

---------

Co-authored-by: Phong Cao Thien [email protected] <[email protected]>
Co-authored-by: Chau Tran <[email protected]>
  • Loading branch information
3 people authored Sep 15, 2023
1 parent c0ca77b commit 7e6cf2e
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 0 deletions.
11 changes: 11 additions & 0 deletions apps/test-app-e2e/src/e2e/if-validator.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
describe('if-validator', () => {
beforeEach(() => cy.visit('/if-validator'));

it('should dynamically change form validation', () => {
cy.get('pre').should('contain.text', 'Is Form Valid: true'); // Default shouldValidate is false, form valid
cy.get('button').click(); // Click the btn change shouldValidate to true
cy.get('pre').should('contain.text', 'Is Form Valid: false'); // Form invalid
cy.get('input').type('[email protected]'); // Type valid email text
cy.get('pre').should('contain.text', 'Is Form Valid: true'); // Form valid
});
});
4 changes: 4 additions & 0 deletions apps/test-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { RouterLink, RouterOutlet } from '@angular/router';
<li>
<a routerLink="/resize">Resize</a>
</li>
<li>
<a routerLink="/if-validator">If Validator</a>
</li>
</ul>
<hr />
Expand Down
4 changes: 4 additions & 0 deletions apps/test-app/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export const appConfig: ApplicationConfig = {
path: 'resize',
loadComponent: () => import('./resize/resize.component'),
},
{
path: 'if-validator',
loadComponent: () => import('./if-validator/if-validator.component'),
},
]),
],
};
34 changes: 34 additions & 0 deletions apps/test-app/src/app/if-validator/if-validator.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { ifValidator } from 'ngxtension/if-validator';

@Component({
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
host: {
style: 'display: block; margin: 12px',
},
template: `
<input [formControl]="form" />
<pre>Is Form Valid: {{ form.valid }}</pre>
<button (click)="changeCondition()">Change Form Condition</button>
`,
})
export default class IfValidator {
public shouldValidate = false;
public form = new FormControl(
null,
ifValidator(
() => this.shouldValidate,
[Validators.required, Validators.email]
)
);

public changeCondition() {
this.shouldValidate = !this.shouldValidate;
this.form.updateValueAndValidity();
}
}
1 change: 1 addition & 0 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default defineConfig({
{ label: 'repeat', link: '/utilities/repeat' },
{ label: 'resize', link: '/utilities/resize' },
{ label: 'createEffect', link: '/utilities/create-effect' },
{ label: 'ifValidator', link: '/utilities/if-validator' },
],
},
],
Expand Down
44 changes: 44 additions & 0 deletions docs/src/content/docs/utilities/if-validator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: ifValidator
description: ngxtension/if-validator
---

`ifValidator` or `ifAsyncValidator` are simple utility functions for help to change dynamically validation of Angular Reactive Form

```ts
import { ifValidator } from 'ngxtension/if-validation';
```

## Usage

`ifValidator` accepts a callback condition and `ValidatorFn` or `ValidatorFn[]`.

```ts
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { ifValidator } from 'ngxtension/if-validator';

@Component({
selector: 'my-app',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<input [formControl]="form" />
<div>Is Form Valid: {{ form.valid }}</div>
<button (click)="changeCondition()">Change Form Condition</button>
`,
})
export class App {
public shouldValidate = true;
public form = new FormControl(
null,
ifValidator(() => this.shouldValidate, [Validators.required, Validators.email])
);

public changeCondition() {
this.shouldValidate = !this.shouldValidate;
this.form.updateValueAndValidity();
}
}
```
5 changes: 5 additions & 0 deletions libs/ngxtension/if-validator/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lib": {
"entryFile": "src/index.ts"
}
}
33 changes: 33 additions & 0 deletions libs/ngxtension/if-validator/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "ngxtension/if-validator",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"sourceRoot": "libs/ngxtension/if-validator/src",
"targets": {
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/ngxtension/jest.config.ts",
"testPathPattern": ["if-validator"],
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"libs/ngxtension/if-validator/**/*.ts",
"libs/ngxtension/if-validator/**/*.html"
]
}
}
}
}
48 changes: 48 additions & 0 deletions libs/ngxtension/if-validator/src/if-validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
AbstractControl,
AsyncValidatorFn,
FormControl,
ValidatorFn,
} from '@angular/forms';
import { of } from 'rxjs';

/**
* Simple Validation with If condition
*/
export function ifValidator(
condition: (control: FormControl) => boolean,
validatorFn: ValidatorFn | ValidatorFn[]
): ValidatorFn {
return (control: AbstractControl) => {
if (!validatorFn || !condition(<FormControl>control)) {
return null;
}

if (validatorFn instanceof Array) {
for (let i = 0; i < validatorFn.length; i++) {
const result = validatorFn[i](control);
if (result) return result;
}

return null;
}

return validatorFn(control);
};
}

/**
* With Async Validation
*/
export function ifAsyncValidator(
condition: (control: FormControl) => boolean,
validatorFn: AsyncValidatorFn
): AsyncValidatorFn {
return (control: AbstractControl) => {
if (!validatorFn || !condition(<FormControl>control)) {
return of(null);
}

return validatorFn(control);
};
}
1 change: 1 addition & 0 deletions libs/ngxtension/if-validator/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './if-validator';
1 change: 1 addition & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
],
"ngxtension/repeat": ["libs/ngxtension/repeat/src/index.ts"],
"ngxtension/resize": ["libs/ngxtension/resize/src/index.ts"],
"ngxtension/if-validator": ["libs/ngxtension/if-validator/src/index.ts"],
"plugin": ["libs/plugin/src/index.ts"]
}
},
Expand Down

0 comments on commit 7e6cf2e

Please sign in to comment.