Gün*_*uer 72
Angular5和6:
angular 5和6推荐的方法是使用@HostBindings和@HostListeners而不是host属性
删除主机并添加 @HostListener
@HostListener('ngModelChange', ['$event'])
onModelChange(event) {
this.onInputChange(event, false);
}
@HostListener('keydown.backspace', ['$event'])
keydownBackspace(event) {
this.onInputChange(event.target.value, true);
}
Run Code Online (Sandbox Code Playgroud)
在线工作stackblitz链接:https://angular6-phone-mask.stackblitz.io
Stackblitz代码示例:https://stackblitz.com/edit/angular6-phone-mask
官方文档链接https://angular.io/guide/attribute-directives#respond-to-user-initiated-events
Angular2和4:
原版的
一种方法是使用注入NgControl和操作值的指令
(有关详细信息,请参阅内联注释)
@Directive({
selector: '[ngModel][phone]',
host: {
'(ngModelChange)': 'onInputChange($event)',
'(keydown.backspace)': 'onInputChange($event.target.value, true)'
}
})
export class PhoneMask {
constructor(public model: NgControl) {}
onInputChange(event, backspace) {
// remove all mask characters (keep only numeric)
var newVal = event.replace(/\D/g, '');
// special handling of backspace necessary otherwise
// deleting of non-numeric characters is not recognized
// this laves room for improvement for example if you delete in the
// middle of the string
if (backspace) {
newVal = newVal.substring(0, newVal.length - 1);
}
// don't show braces for empty value
if (newVal.length == 0) {
newVal = '';
}
// don't show braces for empty groups at the end
else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/, '($1)');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '($1) ($2)');
} else {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(.*)/, '($1) ($2)-$3');
}
// set the new value
this.model.valueAccessor.writeValue(newVal);
}
}
Run Code Online (Sandbox Code Playgroud)
@Component({
selector: 'my-app',
providers: [],
template: `
<form [ngFormModel]="form">
<input type="text" phone [(ngModel)]="data" ngControl="phone">
</form>
`,
directives: [PhoneMask]
})
export class App {
constructor(fb: FormBuilder) {
this.form = fb.group({
phone: ['']
})
}
}
Run Code Online (Sandbox Code Playgroud)
T04*_*435 22
我认为最简单的解决方案是添加ngx-mask
npm i --save ngx-mask
Run Code Online (Sandbox Code Playgroud)
那么你可以做
<input type='text' mask='(000) 000-0000' >
Run Code Online (Sandbox Code Playgroud)
或者
<p>{{ phoneVar | mask: '(000) 000-0000' }} </p>
Run Code Online (Sandbox Code Playgroud)
Luc*_*tto 13
我已经创建了一个通用指令,能够接收任何掩码,并且还能够根据值动态定义掩码:
mask.directive.ts:
import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
import { NgControl } from '@angular/forms';
import { MaskGenerator } from '../interfaces/mask-generator.interface';
@Directive({
selector: '[spMask]'
})
export class MaskDirective {
private static readonly ALPHA = 'A';
private static readonly NUMERIC = '9';
private static readonly ALPHANUMERIC = '?';
private static readonly REGEX_MAP = new Map([
[MaskDirective.ALPHA, /\w/],
[MaskDirective.NUMERIC, /\d/],
[MaskDirective.ALPHANUMERIC, /\w|\d/],
]);
private value: string = null;
private displayValue: string = null;
@Input('spMask')
public maskGenerator: MaskGenerator;
@Input('spKeepMask')
public keepMask: boolean;
@Input('spMaskValue')
public set maskValue(value: string) {
if (value !== this.value) {
this.value = value;
this.defineValue();
}
};
@Output('spMaskValueChange')
public changeEmitter = new EventEmitter<string>();
@HostListener('input', ['$event'])
public onInput(event: { target: { value?: string }}): void {
let target = event.target;
let value = target.value;
this.onValueChange(value);
}
constructor(private ngControl: NgControl) { }
private updateValue(value: string) {
this.value = value;
this.changeEmitter.emit(value);
MaskDirective.delay().then(
() => this.ngControl.control.updateValueAndValidity()
);
}
private defineValue() {
let value: string = this.value;
let displayValue: string = null;
if (this.maskGenerator) {
let mask = this.maskGenerator.generateMask(value);
if (value != null) {
displayValue = MaskDirective.mask(value, mask);
value = MaskDirective.processValue(displayValue, mask, this.keepMask);
}
} else {
displayValue = this.value;
}
MaskDirective.delay().then(() => {
if (this.displayValue !== displayValue) {
this.displayValue = displayValue;
this.ngControl.control.setValue(displayValue);
return MaskDirective.delay();
}
}).then(() => {
if (value != this.value) {
return this.updateValue(value);
}
});
}
private onValueChange(newValue: string) {
if (newValue !== this.displayValue) {
let displayValue = newValue;
let value = newValue;
if ((newValue == null) || (newValue.trim() === '')) {
value = null;
} else if (this.maskGenerator) {
let mask = this.maskGenerator.generateMask(newValue);
displayValue = MaskDirective.mask(newValue, mask);
value = MaskDirective.processValue(displayValue, mask, this.keepMask);
}
this.displayValue = displayValue;
if (newValue !== displayValue) {
this.ngControl.control.setValue(displayValue);
}
if (value !== this.value) {
this.updateValue(value);
}
}
}
private static processValue(displayValue: string, mask: string, keepMask: boolean) {
let value = keepMask ? displayValue : MaskDirective.unmask(displayValue, mask);
return value
}
private static mask(value: string, mask: string): string {
value = value.toString();
let len = value.length;
let maskLen = mask.length;
let pos = 0;
let newValue = '';
for (let i = 0; i < Math.min(len, maskLen); i++) {
let maskChar = mask.charAt(i);
let newChar = value.charAt(pos);
let regex: RegExp = MaskDirective.REGEX_MAP.get(maskChar);
if (regex) {
pos++;
if (regex.test(newChar)) {
newValue += newChar;
} else {
i--;
len--;
}
} else {
if (maskChar === newChar) {
pos++;
} else {
len++;
}
newValue += maskChar;
}
}
return newValue;
}
private static unmask(maskedValue: string, mask: string): string {
let maskLen = (mask && mask.length) || 0;
return maskedValue.split('').filter(
(currChar, idx) => (idx < maskLen) && MaskDirective.REGEX_MAP.has(mask[idx])
).join('');
}
private static delay(ms: number = 0): Promise<void> {
return new Promise(resolve => setTimeout(() => resolve(), ms)).then(() => null);
}
}
Run Code Online (Sandbox Code Playgroud)
(记得在你的NgModule中声明它)
掩码中的数字字符就是9掩码(999) 999-9999.如果需要,可以更改NUMERIC静态字段(例如,如果将其更改为0,则应更改掩码(000) 000-0000).
该值与掩码一起显示,但存储在没有掩码的组件字段中(在我的情况下,这是理想的行为).您可以使用掩码进行存储[spKeepMask]="true".
该指令接收实现该MaskGenerator接口的对象.
面具generator.interface.ts:
export interface MaskGenerator {
generateMask: (value: string) => string;
}
Run Code Online (Sandbox Code Playgroud)
这样就可以根据值动态定义掩码(如信用卡).
我已经创建了一个实用类来存储蒙版,但您也可以直接在组件中指定它.
我-mask.util.ts:
export class MyMaskUtil {
private static PHONE_SMALL = '(999) 999-9999';
private static PHONE_BIG = '(999) 9999-9999';
private static CPF = '999.999.999-99';
private static CNPJ = '99.999.999/9999-99';
public static PHONE_MASK_GENERATOR: MaskGenerator = {
generateMask: () => MyMaskUtil.PHONE_SMALL,
}
public static DYNAMIC_PHONE_MASK_GENERATOR: MaskGenerator = {
generateMask: (value: string) => {
return MyMaskUtil.hasMoreDigits(value, MyMaskUtil.PHONE_SMALL) ?
MyMaskUtil.PHONE_BIG :
MyMaskUtil.PHONE_SMALL;
},
}
public static CPF_MASK_GENERATOR: MaskGenerator = {
generateMask: () => MyMaskUtil.CPF,
}
public static CNPJ_MASK_GENERATOR: MaskGenerator = {
generateMask: () => MyMaskUtil.CNPJ,
}
public static PERSON_MASK_GENERATOR: MaskGenerator = {
generateMask: (value: string) => {
return MyMaskUtil.hasMoreDigits(value, MyMaskUtil.CPF) ?
MyMaskUtil.CNPJ :
MyMaskUtil.CPF;
},
}
private static hasMoreDigits(v01: string, v02: string): boolean {
let d01 = this.onlyDigits(v01);
let d02 = this.onlyDigits(v02);
let len01 = (d01 && d01.length) || 0;
let len02 = (d02 && d02.length) || 0;
let moreDigits = (len01 > len02);
return moreDigits;
}
private static onlyDigits(value: string): string {
let onlyDigits = (value != null) ? value.replace(/\D/g, '') : null;
return onlyDigits;
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以在你的组件中使用它(使用spMaskValue而不是ngModel,但如果不是一个反应形式,使用ngModel什么都没有,就像在下面的例子中,只是为了你不会收到没有提供者的错误,因为注入NgControl了指令;在反应形式中,您不需要包括ngModel):
my.component.ts:
@Component({ ... })
export class MyComponent {
public phoneValue01: string = '1231234567';
public phoneValue02: string;
public phoneMask01 = MyMaskUtil.PHONE_MASK_GENERATOR;
public phoneMask02 = MyMaskUtil.DYNAMIC_PHONE_MASK_GENERATOR;
}
Run Code Online (Sandbox Code Playgroud)
my.component.html:
<span>Phone 01 ({{ phoneValue01 }}):</span><br>
<input type="text" [(spMaskValue)]="phoneValue01" [spMask]="phoneMask01" ngModel>
<br><br>
<span>Phone 02 ({{ phoneValue02 }}):</span><br>
<input type="text" [(spMaskValue)]="phoneValue02" [spMask]="phoneMask02" [spKeepMask]="true" ngModel>
Run Code Online (Sandbox Code Playgroud)
(看一看,phone02看到当你再输入1个数字时,掩码会改变;另外,看看存储的值phone01是否没有掩码)
我用普通输入和ionic输入(ion-input)测试了它,包括反应性(有formControlName,没有formControl)和非反应形式.
我使用' angular2-text-mask '中的TextMaskModule来做到这一点
我的分裂,但你可以得到这个想法
使用NPM NodeJS打包
"dependencies": {
"angular2-text-mask": "8.0.0",
Run Code Online (Sandbox Code Playgroud)
HTML
<input *ngIf="column?.type =='areaCode'" type="text" [textMask]="{mask: areaCodeMask}" [(ngModel)]="areaCodeModel">
<input *ngIf="column?.type =='phone'" type="text" [textMask]="{mask: phoneMask}" [(ngModel)]="phoneModel">
Run Code Online (Sandbox Code Playgroud)
内部组件
public areaCodeModel = '';
public areaCodeMask = ['(', /[1-9]/, /\d/, /\d/, ')'];
public phoneModel = '';
public phoneMask = [/\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/];
Run Code Online (Sandbox Code Playgroud)
除了@GünterZöchbauer上面的答案,我尝试了如下,它似乎工作但我不确定它是否是一种有效的方式.
我使用valueChangesobservable通过订阅来监听被动形式的变更事件.对于退格的特殊处理,我data从订阅中获取并检查它userForm.value.phone(from [formGroup]="userForm").因为,在那一刻,数据变为新值,但后者由于尚未设置而引用之前的值.如果数据小于先前值,则用户应从输入中删除字符.在这种情况下,更改模式如下:
来自: newVal = newVal.replace(/^(\d{0,3})/, '($1)');
至 : newVal = newVal.replace(/^(\d{0,3})/, '($1');
否则,正如上面提到的GünterZöchbauer所示,无法识别非数字字符的删除,因为当我们从输入中删除括号时,数字仍然保持不变并再次添加来自模式匹配的括号.
控制器:
import { Component,OnInit } from '@angular/core';
import { FormGroup,FormBuilder,AbstractControl,Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
constructor(private fb:FormBuilder) {
this.createForm();
}
createForm(){
this.userForm = this.fb.group({
phone:['',[Validators.pattern(/^\(\d{3}\)\s\d{3}-\d{4}$/),Validators.required]],
});
}
ngOnInit() {
this.phoneValidate();
}
phoneValidate(){
const phoneControl:AbstractControl = this.userForm.controls['phone'];
phoneControl.valueChanges.subscribe(data => {
/**the most of code from @Günter Zöchbauer's answer.*/
/**we remove from input but:
@preInputValue still keep the previous value because of not setting.
*/
let preInputValue:string = this.userForm.value.phone;
let lastChar:string = preInputValue.substr(preInputValue.length - 1);
var newVal = data.replace(/\D/g, '');
//when removed value from input
if (data.length < preInputValue.length) {
/**while removing if we encounter ) character,
then remove the last digit too.*/
if(lastChar == ')'){
newVal = newVal.substr(0,newVal.length-1);
}
if (newVal.length == 0) {
newVal = '';
}
else if (newVal.length <= 3) {
/**when removing, we change pattern match.
"otherwise deleting of non-numeric characters is not recognized"*/
newVal = newVal.replace(/^(\d{0,3})/, '($1');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '($1) $2');
} else {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(.*)/, '($1) $2-$3');
}
//when typed value in input
} else{
if (newVal.length == 0) {
newVal = '';
}
else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/, '($1)');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '($1) $2');
} else {
newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(.*)/, '($1) $2-$3');
}
}
this.userForm.controls['phone'].setValue(newVal,{emitEvent: false});
});
}
}
Run Code Online (Sandbox Code Playgroud)
模板:
<form [formGroup]="userForm" novalidate>
<div class="form-group">
<label for="tel">Tel:</label>
<input id="tel" formControlName="phone" maxlength="14">
</div>
<button [disabled]="userForm.status == 'INVALID'" type="submit">Send</button>
</form>
Run Code Online (Sandbox Code Playgroud)
UPDATE
有没有办法在字符串中间退格时保留光标位置?目前,它跳回到最后.
定义id <input id="tel" formControlName="phone" #phoneRef>
和renderer2#selectRootElement以获取组件中的本机元素.
所以我们可以使用以下方法获取光标位置
let start = this.renderer.selectRootElement('#tel').selectionStart;
let end = this.renderer.selectRootElement('#tel').selectionEnd;
Run Code Online (Sandbox Code Playgroud)
然后我们可以在输入更新为新值后应用它:
this.userForm.controls['phone'].setValue(newVal,{emitEvent: false});
//keep cursor the appropriate position after setting the input above.
this.renderer.selectRootElement('#tel').setSelectionRange(start,end);
Run Code Online (Sandbox Code Playgroud)
更新2
将这种逻辑放在指令而不是组件中可能更好
我还将逻辑放入指令中.这样可以更轻松地将其应用于其他元素.
注意:它特定于(123) 123-4567模式.
| 归档时间: |
|
| 查看次数: |
87272 次 |
| 最近记录: |