在 Angular 组件包装器中创建 Phone 文本框
Create Phone Textbox into Angular Component Wrapper
我正在尝试将 Phone 掩码从下面的解决方案转换为 Angular 组件。有谁知道如何进行这个? * 任何为 Phone 文本框创建 1 个相似组件的答案都可以。
Mask for an Input to allow phone numbers?
https://stackblitz.com/edit/angular6-phone-mask
我试过了,将代码复制到下面的组件中。接收错误,
phonebox 允许文本超过 10 个字符。
调试时,退格所有字符时,一个字符值
仍然存在。
原来的答案使用指令,并且只适用于formcontrol。目标是让自定义公司文本框组件具有唯一的样式、输入等。
在底部,我们引用了 stackblitz 代码。
打字稿:
export class CustomFieldErrorMatcher implements ErrorStateMatcher {
constructor(private customControl: FormControl,private errors:any) { }
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return this.customControl && this.customControl.touched &&(this.customControl.invalid || this.errors);
}
}
@Component({
selector: 'app-input-phone',
templateUrl: './input-phone.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputPhoneComponent),
multi: true
}
]})
export class InputPhoneComponent implements OnInit, ControlValueAccessor {
@Input() MaxLength: string;
@Input() ReadOnly: boolean;
@Input() Value: string;
@Input() type: string;
@Input() Label: string;
@Input() PlaceHolder: string;
@Output() saveValue = new EventEmitter();
@Output() onStateChange = new EventEmitter();
@Input() errors: any = null;
disabled: boolean;
control: FormControl;
constructor(public injector: Injector) {}
ngOnInit() { }
ngAfterViewInit(): void {
const ngControl: NgControl = this.injector.get(NgControl, null);
if (ngControl) {
setTimeout(() => {
this.control = ngControl.control as FormControl;
})
}}
saveValueAction(e) { this.saveValue.emit(e.target.value); }
writeValue(value: any) { this.Value = value ? value : ''; }
onChange(e) { this.Value = e; }
onTouched() { this.onStateChange.emit(); }
registerOnChange(fn: any) { this.onChange = fn; }
registerOnTouched(fn: any) { this.onTouched = fn; }
setDisabledState(isDisabled) { this.disabled = isDisabled; }
errorMatcher() {
return new CustomFieldErrorMatcher(this.control,this.errors)
}
readonly errorStateMatcher: ErrorStateMatcher = {
isErrorState: (ctrl: FormControl) => (ctrl && ctrl.invalid)
};
//Section from Stackblitz reference
@HostListener('ngModelChange', ['$event'])
onModelChange(event) {
this.onInputChange(event, false);
}
@HostListener('keydown.backspace', ['$event'])
keydownBackspace(event) {
this.onInputChange(event.target.value, true);
}
onInputChange(event, backspace) {
let newVal = event.replace(/\D/g, '');
if (backspace && newVal.length <= 6) {
newVal = newVal.substring(0, newVal.length - 1);
}
if (newVal.length === 0) {
newVal = '';
} else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/, '()');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '() ()');
} else if (newVal.length <= 10) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
} else {
newVal = newVal.substring(0, 10);
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
}
this.writeValue(newVal);
}
}
HTML:
<div class="input-wrap">
<mat-form-field>
<mat-label>{{Label}}</mat-label>
<input
matInput
[attr.maxlength] = "MaxLength"
[value]="Value ? Value : ''"
[placeholder]="PlaceHolder ? PlaceHolder : ''"
[readonly]="ReadOnly"
[type]="type ? type: 'text'"
[ngModel]="Value"
[errorStateMatcher]="errorMatcher()"
(input)="onChange($event.target.value)"
(blur)="onTouched()"
(change)="saveValueAction($event)"
(ngModelChange)="Value=$event"
>
</mat-form-field>
</div>
Working Forked StackBlitz
打字稿:
import { Component, forwardRef, OnInit, Input, Output, EventEmitter, Injector, HostListener, ViewChild, ElementRef } from '@angular/core';
import { FormControl, FormGroupDirective, NgForm, NG_VALUE_ACCESSOR, ControlValueAccessor, NgControl } from '@angular/forms';
export interface ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean;
}
export class CustomFieldErrorMatcher implements ErrorStateMatcher {
constructor(private customControl: FormControl, private errors: any) { }
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return this.customControl && this.customControl.touched && (this.customControl.invalid || this.errors);
}
}
@Component({
selector: 'app-input-phone',
templateUrl: './input-phone.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputPhoneComponent),
multi: true
}
]
})
export class InputPhoneComponent implements OnInit, ControlValueAccessor {
disabled: boolean;
control: FormControl;
@Input() MaxLength: string;
@Input() ReadOnly: boolean;
@Input() value: string;
@Input() type: string;
@Input() Label: string;
@Input() PlaceHolder: string;
@Output() saveValue = new EventEmitter();
@Output() stateChange = new EventEmitter();
@Input() errors: any = null;
@ViewChild('input', { static: true }) inputViewChild: ElementRef;
readonly errorStateMatcher: ErrorStateMatcher = {
isErrorState: (ctrl: FormControl) => (ctrl && ctrl.invalid)
};
constructor(public injector: Injector) { }
ngOnInit() { }
saveValueAction(e: any) { this.saveValue.emit(e.target.value); }
writeValue(value: any) {
this.value = value ? value : '';
if (this.inputViewChild && this.inputViewChild.nativeElement) {
if (this.value === undefined || this.value == null) {
this.inputViewChild.nativeElement.value = '';
} else {
const maskValue = this.convertToMaskValue(this.value, false);
this.inputViewChild.nativeElement.value = maskValue;
}
}
}
onModelChange: Function = () => { };
onChange(e) { this.value = e; }
onTouched() { this.stateChange.emit(); }
registerOnChange(fn: () => void): void {
this.onModelChange = fn;
}
registerOnTouched(fn: any) { this.onTouched = fn; }
setDisabledState(isDisabled) { this.disabled = isDisabled; }
errorMatcher() {
return new CustomFieldErrorMatcher(this.control, this.errors);
}
onInputChange(event) {
setTimeout(() => {
const maskValue = this.convertToMaskValue(event.target.value, event.inputType === 'deleteContentBackward');
this.inputViewChild.nativeElement.value = maskValue;
this.value = this.convertToRealValue(maskValue);
this.onModelChange(this.value);
}, 0);
}
private convertToMaskValue(value: string, backspace: boolean): string {
let newVal = value;
if (newVal && newVal.length > 0) {
if (backspace && value.length <= 12) {
newVal = value.substring(0, value.length - 1);
}
newVal = this.convertToRealValue(newVal);
if (newVal.length === 0) {
newVal = '';
} else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/, '()');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '() ()');
} else if (newVal.length <= 10) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
} else {
newVal = newVal.substring(0, 10);
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
}
}
return newVal;
}
private convertToRealValue(value: string): string {
return value.replace(/\D/g, '');
}
}
HTML:
<div class="input-wrap">
<mat-form-field>
<mat-label>{{Label}}</mat-label>
<input #input matInput [attr.maxlength]="MaxLength" [placeholder]="PlaceHolder ? PlaceHolder : ''"
[readonly]="ReadOnly" [type]="type ? type: 'text'"
(input)="onInputChange($event)" (blur)="onTouched()">
</mat-form-field>
</div>
我正在尝试将 Phone 掩码从下面的解决方案转换为 Angular 组件。有谁知道如何进行这个? * 任何为 Phone 文本框创建 1 个相似组件的答案都可以。
Mask for an Input to allow phone numbers?
https://stackblitz.com/edit/angular6-phone-mask
我试过了,将代码复制到下面的组件中。接收错误,
phonebox 允许文本超过 10 个字符。
调试时,退格所有字符时,一个字符值 仍然存在。
原来的答案使用指令,并且只适用于formcontrol。目标是让自定义公司文本框组件具有唯一的样式、输入等。
在底部,我们引用了 stackblitz 代码。
打字稿:
export class CustomFieldErrorMatcher implements ErrorStateMatcher {
constructor(private customControl: FormControl,private errors:any) { }
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return this.customControl && this.customControl.touched &&(this.customControl.invalid || this.errors);
}
}
@Component({
selector: 'app-input-phone',
templateUrl: './input-phone.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputPhoneComponent),
multi: true
}
]})
export class InputPhoneComponent implements OnInit, ControlValueAccessor {
@Input() MaxLength: string;
@Input() ReadOnly: boolean;
@Input() Value: string;
@Input() type: string;
@Input() Label: string;
@Input() PlaceHolder: string;
@Output() saveValue = new EventEmitter();
@Output() onStateChange = new EventEmitter();
@Input() errors: any = null;
disabled: boolean;
control: FormControl;
constructor(public injector: Injector) {}
ngOnInit() { }
ngAfterViewInit(): void {
const ngControl: NgControl = this.injector.get(NgControl, null);
if (ngControl) {
setTimeout(() => {
this.control = ngControl.control as FormControl;
})
}}
saveValueAction(e) { this.saveValue.emit(e.target.value); }
writeValue(value: any) { this.Value = value ? value : ''; }
onChange(e) { this.Value = e; }
onTouched() { this.onStateChange.emit(); }
registerOnChange(fn: any) { this.onChange = fn; }
registerOnTouched(fn: any) { this.onTouched = fn; }
setDisabledState(isDisabled) { this.disabled = isDisabled; }
errorMatcher() {
return new CustomFieldErrorMatcher(this.control,this.errors)
}
readonly errorStateMatcher: ErrorStateMatcher = {
isErrorState: (ctrl: FormControl) => (ctrl && ctrl.invalid)
};
//Section from Stackblitz reference
@HostListener('ngModelChange', ['$event'])
onModelChange(event) {
this.onInputChange(event, false);
}
@HostListener('keydown.backspace', ['$event'])
keydownBackspace(event) {
this.onInputChange(event.target.value, true);
}
onInputChange(event, backspace) {
let newVal = event.replace(/\D/g, '');
if (backspace && newVal.length <= 6) {
newVal = newVal.substring(0, newVal.length - 1);
}
if (newVal.length === 0) {
newVal = '';
} else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/, '()');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '() ()');
} else if (newVal.length <= 10) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
} else {
newVal = newVal.substring(0, 10);
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
}
this.writeValue(newVal);
}
}
HTML:
<div class="input-wrap">
<mat-form-field>
<mat-label>{{Label}}</mat-label>
<input
matInput
[attr.maxlength] = "MaxLength"
[value]="Value ? Value : ''"
[placeholder]="PlaceHolder ? PlaceHolder : ''"
[readonly]="ReadOnly"
[type]="type ? type: 'text'"
[ngModel]="Value"
[errorStateMatcher]="errorMatcher()"
(input)="onChange($event.target.value)"
(blur)="onTouched()"
(change)="saveValueAction($event)"
(ngModelChange)="Value=$event"
>
</mat-form-field>
</div>
Working Forked StackBlitz
打字稿:
import { Component, forwardRef, OnInit, Input, Output, EventEmitter, Injector, HostListener, ViewChild, ElementRef } from '@angular/core';
import { FormControl, FormGroupDirective, NgForm, NG_VALUE_ACCESSOR, ControlValueAccessor, NgControl } from '@angular/forms';
export interface ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean;
}
export class CustomFieldErrorMatcher implements ErrorStateMatcher {
constructor(private customControl: FormControl, private errors: any) { }
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return this.customControl && this.customControl.touched && (this.customControl.invalid || this.errors);
}
}
@Component({
selector: 'app-input-phone',
templateUrl: './input-phone.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputPhoneComponent),
multi: true
}
]
})
export class InputPhoneComponent implements OnInit, ControlValueAccessor {
disabled: boolean;
control: FormControl;
@Input() MaxLength: string;
@Input() ReadOnly: boolean;
@Input() value: string;
@Input() type: string;
@Input() Label: string;
@Input() PlaceHolder: string;
@Output() saveValue = new EventEmitter();
@Output() stateChange = new EventEmitter();
@Input() errors: any = null;
@ViewChild('input', { static: true }) inputViewChild: ElementRef;
readonly errorStateMatcher: ErrorStateMatcher = {
isErrorState: (ctrl: FormControl) => (ctrl && ctrl.invalid)
};
constructor(public injector: Injector) { }
ngOnInit() { }
saveValueAction(e: any) { this.saveValue.emit(e.target.value); }
writeValue(value: any) {
this.value = value ? value : '';
if (this.inputViewChild && this.inputViewChild.nativeElement) {
if (this.value === undefined || this.value == null) {
this.inputViewChild.nativeElement.value = '';
} else {
const maskValue = this.convertToMaskValue(this.value, false);
this.inputViewChild.nativeElement.value = maskValue;
}
}
}
onModelChange: Function = () => { };
onChange(e) { this.value = e; }
onTouched() { this.stateChange.emit(); }
registerOnChange(fn: () => void): void {
this.onModelChange = fn;
}
registerOnTouched(fn: any) { this.onTouched = fn; }
setDisabledState(isDisabled) { this.disabled = isDisabled; }
errorMatcher() {
return new CustomFieldErrorMatcher(this.control, this.errors);
}
onInputChange(event) {
setTimeout(() => {
const maskValue = this.convertToMaskValue(event.target.value, event.inputType === 'deleteContentBackward');
this.inputViewChild.nativeElement.value = maskValue;
this.value = this.convertToRealValue(maskValue);
this.onModelChange(this.value);
}, 0);
}
private convertToMaskValue(value: string, backspace: boolean): string {
let newVal = value;
if (newVal && newVal.length > 0) {
if (backspace && value.length <= 12) {
newVal = value.substring(0, value.length - 1);
}
newVal = this.convertToRealValue(newVal);
if (newVal.length === 0) {
newVal = '';
} else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/, '()');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '() ()');
} else if (newVal.length <= 10) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
} else {
newVal = newVal.substring(0, 10);
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '() ()-');
}
}
return newVal;
}
private convertToRealValue(value: string): string {
return value.replace(/\D/g, '');
}
}
HTML:
<div class="input-wrap">
<mat-form-field>
<mat-label>{{Label}}</mat-label>
<input #input matInput [attr.maxlength]="MaxLength" [placeholder]="PlaceHolder ? PlaceHolder : ''"
[readonly]="ReadOnly" [type]="type ? type: 'text'"
(input)="onInputChange($event)" (blur)="onTouched()">
</mat-form-field>
</div>