【问题标题】:Create Phone Textbox into Angular Component Wrapper在 Angular 组件包装器中创建电话文本框
【发布时间】:2019-12-04 06:12:57
【问题描述】:

我正在尝试将 Phone Mask 从以下解决方案转换为 Angular 组件。有谁知道如何进行? * 任何为 Phone 文本框创建 1 个类似组件的答案都可以使用。

Mask for an Input to allow phone numbers?

https://stackblitz.com/edit/angular6-phone-mask

我试过,将代码复制到下面的组件中。接收错误,

  • phonebox 允许文本超过 10 个字符。

  • 在调试中,退格所有字符时,一个字符值 仍然存在。

原始答案使用指令,并且仅适用于表单控件。目标是拥有具有唯一样式、输入等的自定义公司文本框组件。

在底部,我们引用了 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})/, '($1)');
    } else if (newVal.length <= 6) {
      newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '($1) ($2)');
    } else if (newVal.length <= 10) {
      newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '($1) ($2)-$3');
    } else {
      newVal = newVal.substring(0, 10);
      newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '($1) ($2)-$3');
    }
    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>

【问题讨论】:

  • 你能在stackblitz中提供一个演示吗?即您尝试过的代码
  • 需要代码才能接受作为答案,谢谢
  • 代码应该能够在没有 FormControlName 的情况下工作,
  • 嗨,马特,你能提供你的工作 StackBlitz 吗?
  • stackblitz.com/edit/angular6-phone-mask 这需要转入1个组件

标签: angular typescript angular-material angular7


【解决方案1】:

工作Forked StackBlitz

TypeScript:

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})/, '($1)');
      } else if (newVal.length <= 6) {
        newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '($1) ($2)');
      } else if (newVal.length <= 10) {
        newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '($1) ($2)-$3');
      } else {
        newVal = newVal.substring(0, 10);
        newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})/, '($1) ($2)-$3');
      }
    }
    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>

【讨论】:

    猜你喜欢
    • 2020-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 2010-12-04
    • 1970-01-01
    • 2016-01-07
    • 2018-08-02
    相关资源
    最近更新 更多