Skip to content

Commit

Permalink
add suport for pristine on init (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
NepipenkoIgor committed Feb 21, 2018
1 parent 1a8f899 commit 1c3c564
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 68 deletions.
5 changes: 4 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ export class AppComponent {
public clearIfNotMatchModel: string;

public constructor() {
this.form = new FormControl('30081991');
this.form = new FormControl('23234234');
setTimeout(() => {
this.form.reset();
}, 5000);
this.cpfFormControl = new FormControl('04787954778');
this.clearIfNotMatchForm = new FormControl();
}
Expand Down
20 changes: 20 additions & 0 deletions src/app/ngx-mask/mask.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ describe('Directive: Mask', () => {
.toBe(expectedValue);
}

it('Pristine and dirty', () => {
component.mask = '0000.0000';
expect(component.form.dirty)
.toBeFalsy();
expect(component.form.pristine)
.toBeTruthy();
equal('1.', '1');
equal('1éáa2aaaaqwo', '12');
equal('1234567', '1234.567');
expect(component.form.dirty)
.toBeTruthy();
expect(component.form.pristine)
.toBeFalsy();
component.form.reset();
expect(component.form.dirty)
.toBeFalsy();
expect(component.form.pristine)
.toBeTruthy();
});

it('When I change the mask on-the-fly things should work normally', () => {
component.mask = '0000.0000';
equal('1.', '1');
Expand Down
45 changes: 5 additions & 40 deletions src/app/ngx-mask/mask.directive.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
import {
Directive, ElementRef, forwardRef, HostListener, Input, OnInit,
Renderer2
Directive, HostListener, Input
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { MaskService } from './mask.service';
import { IConfig } from './config';

const resolvedPromise: Promise<null> = Promise.resolve(null);

@Directive({
selector: '[mask]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MaskDirective),
useExisting: MaskService,
multi: true
},
MaskService
],
})
export class MaskDirective implements OnInit, ControlValueAccessor {
export class MaskDirective {

private _maskValue: string;

public constructor(
private _elementRef: ElementRef,
private _renderer: Renderer2,
private _maskService: MaskService,
) { }

public ngOnInit(): void {
resolvedPromise.then(() => this._maskService.applyValueChanges(this._elementRef.nativeElement));
}

@Input('mask')
public set maskExpression(value: string) {
this._maskValue = value || '';
Expand Down Expand Up @@ -78,7 +69,6 @@ export class MaskDirective implements OnInit, ControlValueAccessor {

let caretShift: number = 0;
this._maskService.applyValueChanges(
this._elementRef.nativeElement,
position,
(shift: number) => caretShift = shift
);
Expand All @@ -92,32 +82,7 @@ export class MaskDirective implements OnInit, ControlValueAccessor {

@HostListener('blur')
public onBlur(): void {
this._maskService.clearIfNotMatchFn(this._elementRef.nativeElement);
this._maskService.applyValueChanges(this._elementRef.nativeElement);
this._maskService.clearIfNotMatchFn();
this._maskService.onTouch();
}

/** It writes the value in the input */
public writeValue(inputValue: string): void {
this._elementRef.nativeElement.value = this._maskService.applyMask(inputValue, this._maskService.maskExpression);
this._maskService.applyValueChanges(this._elementRef.nativeElement);
}

// tslint:disable-next-line
public registerOnChange(fn: any): void {
this._maskService.onChange = fn;
}

// tslint:disable-next-line
public registerOnTouched(fn: any): void {
this._maskService.onTouch = fn;
}

/** It disables the input element */
public setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
return this._renderer.setAttribute(this._elementRef.nativeElement, 'disabled', 'true');
}
return this._renderer.removeAttribute(this._elementRef.nativeElement, 'disabled');
}
}
94 changes: 67 additions & 27 deletions src/app/ngx-mask/mask.service.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import { Inject, Injectable } from '@angular/core';
import { ElementRef, Inject, Injectable, Renderer2 } from '@angular/core';
import { config, IConfig } from './config';
import { DOCUMENT } from '@angular/common';
import { ControlValueAccessor } from '@angular/forms';

@Injectable()
export class MaskService {
export class MaskService implements ControlValueAccessor {

public dropSpecialCharacters: IConfig['dropSpecialCharacters'];
public clearIfNotMatch: IConfig['clearIfNotMatch'];
public maskExpression: string = '';
public maskSpecialCharacters: IConfig['specialCharacters'];
public maskAvailablePatterns: IConfig['patterns'];

private _regExpForRemove: RegExp;
private _shift: Set<number>;
private _formElement: HTMLInputElement;

// tslint:disable-next-line
public onChange = (_: any) => { };

public onTouch = () => { };


public constructor(
// tslint:disable-next-line
@Inject(DOCUMENT) private document: any,
@Inject(config) private _config: IConfig,
private _elementRef: ElementRef,
private _renderer: Renderer2,
) {
this._shift = new Set();
this.clearIfNotMatch = this._config.clearIfNotMatch;
Expand All @@ -25,12 +37,9 @@ export class MaskService {
this._regExpForRemove = new RegExp(this.maskSpecialCharacters
.map((item: string) => `\\${item}`)
.join('|'), 'gi');
}

// tslint:disable-next-line
public onChange = (_: any) => { };

public onTouch = () => { };
this._formElement = this._elementRef.nativeElement;
}

public applyMask(inputValue: string, maskExpression: string, position: number = 0, cb: Function = () => { }): string {
if (inputValue === undefined || inputValue === null) {
Expand All @@ -44,7 +53,7 @@ export class MaskService {
.split('');
// tslint:disable-next-line
for (let i: number = 0, inputSymbol: string = inputArray[0]; i
< inputArray.length; i++ , inputSymbol = inputArray[i]) {
< inputArray.length; i++ , inputSymbol = inputArray[i]) {
if (result.length === maskExpression.length) {
break;
}
Expand Down Expand Up @@ -79,35 +88,61 @@ export class MaskService {
return result;
}

public applyValueChanges(element: HTMLInputElement, position: number = 0, cb: Function = () => { }): void {
const val: string = element.value;
const maskedInput: string = this.applyMask(val, this.maskExpression, position, cb);
public applyValueChanges(position: number = 0, cb: Function = () => { }): void {
const maskedInput: string = this.applyMask(this._formElement.value, this.maskExpression, position, cb);

element.value = maskedInput;
this._formElement.value = maskedInput;

if (this.dropSpecialCharacters === true) {
this.onChange(this._removeMask(maskedInput));
} else {
this.onChange(maskedInput);
}
this.dropSpecialCharacters === true
? this.onChange(this._removeMask(maskedInput))
: this.onChange(maskedInput);

if (element !== this.document.activeElement) {
this.clearIfNotMatchFn(element);
if (this._formElement === this.document.activeElement) {
return;
}
this.clearIfNotMatchFn();
}

public clearIfNotMatchFn(element: HTMLInputElement): void {
if (this.clearIfNotMatch === true && this.maskExpression.length
!== element.value.length) {
element.value = '';
public clearIfNotMatchFn(): void {
if (
this.clearIfNotMatch === true && this.maskExpression.length
!== this._formElement.value.length) {
this._formElementProperty = ['value', ''];
}
}


/** It writes the value in the input */
public writeValue(inputValue: string): void {
/**
* FIXME init before mask expretion;
*/
inputValue
? this._formElementProperty = ['value', this.applyMask(inputValue, this.maskExpression)]
: this._formElementProperty = ['value', ''];
}

// tslint:disable-next-line
public registerOnChange(fn: any): void {
this.onChange = fn;
}

// tslint:disable-next-line
public registerOnTouched(fn: any): void {
this.onTouch = fn;
}

/** It disables the input element */
public setDisabledState(isDisabled: boolean): void {
isDisabled
? this._formElementProperty = ['disabled', 'true']
: this._formElementProperty = ['disabled', 'false'];

This comment has been minimized.

Copy link
@c-ice

c-ice Apr 5, 2018

this doesnt work, because disabled atribute is still in DOM,
check the previous approach - 1c3c564#diff-14acf2a9e06ba82395f56e0a798b0dc3L118

}

private _removeMask(value: string): string {
if (!value) {
return value;
}
return value.replace(this._regExpForRemove, '');
return value
? value.replace(this._regExpForRemove, '')
: value;
}

private _checkSymbolMask(inputSymbol: string, maskSymbol: string): boolean {
Expand All @@ -116,4 +151,9 @@ export class MaskService {
|| this.maskAvailablePatterns[maskSymbol] && this.maskAvailablePatterns[maskSymbol].pattern
&& this.maskAvailablePatterns[maskSymbol].pattern.test(inputSymbol);
}

private set _formElementProperty([name, value]: [string, string]) {
this._renderer.setProperty(this._formElement, name, value);
}

}

0 comments on commit 1c3c564

Please sign in to comment.