Skip to content

Commit

Permalink
refactor: remove CVA and inject NgControl instead
Browse files Browse the repository at this point in the history
  • Loading branch information
mrgleam committed Feb 18, 2022
1 parent 8bf7dfd commit 42dde9c
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 75 deletions.
13 changes: 2 additions & 11 deletions projects/ngx-english-only-app/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -481,17 +481,8 @@ <h2>Next Steps</h2>
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->

<div>
<label for="name">Name: </label>
</div>
<textarea id="name" rows="3" [formControl]="name" englishOnly></textarea>

<div>
<label for="name">Email: </label>
</div>
<div>
<input type="text" [formControl]="email">
</div>
<form [formGroup] ="nestedForm">
<my-input formControlName="basicInfo"></my-input>
<button>submit</button>

<router-outlet></router-outlet>
12 changes: 7 additions & 5 deletions projects/ngx-english-only-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
selector: 'app-root',
Expand All @@ -8,12 +8,14 @@ import { FormControl, Validators } from '@angular/forms';
})
export class AppComponent {
title = 'ngx-english-only-app';
name = new FormControl('', Validators.required);
email = new FormControl('', Validators.email)
public nestedForm: FormGroup = new FormGroup({
basicInfo: new FormControl("")
});

constructor() {
this.name.setValue('test');
}

ngOnInit(): void {
this.name.valueChanges.subscribe(v => console.log(v))
this.nestedForm.valueChanges.subscribe(v => console.log(v))
}
}
9 changes: 7 additions & 2 deletions projects/ngx-english-only-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { NgxEnglishOnlyModule } from 'projects/ngx-english-only/src/public-api';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MyInputComponent } from './my-input.component';

@NgModule({
declarations: [
AppComponent
AppComponent,
MyInputComponent
],
imports: [
CommonModule,
BrowserModule,
AppRoutingModule,
FormsModule,
ReactiveFormsModule,
NgxEnglishOnlyModule
],
Expand Down
60 changes: 60 additions & 0 deletions projects/ngx-english-only-app/src/app/my-input.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Component, forwardRef, OnInit } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup, FormControl, NG_VALIDATORS, Validator, AbstractControl, ValidationErrors } from '@angular/forms';

@Component({
selector: 'my-input',
template:`
<ng-container [formGroup]="basicInfoForm">
<label for="Full Name"> Full Name </label>
<div>
<textarea rows="3" formControlName="fname" englishOnly></textarea>
</div>
<label for="Email"> Email </label>
<div>
<input type="email" formControlName="email">
</div>
</ng-container>`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MyInputComponent ),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MyInputComponent),
multi: true
}
]
})

export class MyInputComponent implements OnInit, ControlValueAccessor, Validator {
public basicInfoForm: FormGroup = new FormGroup(
{
fname: new FormControl(""),
email: new FormControl("")
});
constructor() { }

ngOnInit() {
}

public onTouched: () => void = () => {};

writeValue(val: any): void {
val && this.basicInfoForm.setValue(val, { emitEvent: false });
}
registerOnChange(fn: any): void {
this.basicInfoForm.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.basicInfoForm.disable() : this.basicInfoForm.enable();
}

validate(c: AbstractControl): ValidationErrors | null{
return this.basicInfoForm.valid ? null : { invalidForm: {valid: false, message: "basicInfoForm fields are invalid"}};
}
}
2 changes: 1 addition & 1 deletion projects/ngx-english-only/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngx-english-only",
"version": "0.0.1",
"version": "0.0.2",
"license": "MIT",
"author": "Santi Lertsumran <santi.lertsumran@gmail.com>",
"repository": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,79 +1,32 @@
import { Directive, ElementRef, forwardRef, HostListener, Renderer2 } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
selector: '[englishOnly]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NgxEnglishOnlyDirective),
multi: true
}
],
selector: '[englishOnly]'
})
export class NgxEnglishOnlyDirective implements ControlValueAccessor {
private isDisabled = false;
export class NgxEnglishOnlyDirective {
private readonly allowSpecialKeys: Array<string> = ['Backspace', 'Tab', 'End', 'Home', '-', 'ArrowRight', 'ArrowLeft'];

private readonly REGEX_EN: RegExp = /^[a-zA-Z0-9.\s?><;:",{}()[\]\-_+=!@#$%\^’‘&*|'/\\]*$/g;

onChangeFn: (value: any) => void = () => {
};

onTouchedFn = () => {
};

constructor(private elm: ElementRef, private renderer: Renderer2) { }
constructor(private control: NgControl) { }

@HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.allowSpecialKeys.indexOf(event.key) !== -1) {
return;
}

const current: string = this.elm.nativeElement.value;
const current: string = this.control.control?.value;
const next: string = current.concat(event.key);

if (next && !String(next).match(this.REGEX_EN)) {
event.preventDefault();
}
}

@HostListener('blur', ['$event.target.value'])
onBlur(value: string) {
const replaced = Array.from(value).filter(v => v.match(this.REGEX_EN)).join('');
this.elm.nativeElement.value = replaced;
this.onChangeFn(replaced);
this.onTouchedFn();
}

@HostListener('paste', ['$event'])
onPaste(event: ClipboardEvent) {
const clipboardData: DataTransfer | null = event.clipboardData;
const pastedDate: string = clipboardData?.getData('Text') || '';
const current: string = this.elm.nativeElement.value;
const replaced = Array.from(current?.concat(pastedDate)).filter(v => v.match(this.REGEX_EN)).join('');
this.onChangeFn(replaced);
}

writeValue(value: string): void {
this.elm.nativeElement.value = value;
}

registerOnChange(fn: any): void {
this.onChangeFn = fn;
}

registerOnTouched(fn: () => void): void {
this.onTouchedFn = fn;
}

setDisabledState(isDisabled: boolean): void {
this.isDisabled = isDisabled;
if (this.isDisabled) {
this.renderer.setProperty(this.elm.nativeElement, 'disabled', 'disabled');
} else {
this.renderer.removeAttribute(this.elm.nativeElement, 'disabled');
}
@HostListener('input', ['$event'])
onInputChange() {
this.control.control?.setValue(Array.from(this.control.control?.value as string || '').filter(v => v.match(this.REGEX_EN)).join(''));
}
}

0 comments on commit 42dde9c

Please sign in to comment.