在平时开发的过程中,产品终会提一些另辟蹊径的建议,例如我们在做输入限制的时候,产品希望可以根据输入字符汉字不同去做不同限制;例如:限制输入10个字符或者5个汉字
// 嫌弃代码不全的队友请放心,下面会有完整的代码
// @HostListener('compositionstart')
onCompositionstart() {
this.isComposite = true;
}
// @HostListener('compositionEnd', ['$event'])
onCompositionEnd($event) {
this.isComposite = false;
this.onInput($event);
}
// @HostListener('input', ['$event'])
onInput($event) {
if (!$event || this.isComposite) {
return;
}
const target = $event.target;
if (this.CN) {
target.value = this.limitLength(target.value, this.maxLen);
this.val = this.limitLength(target.value, this.maxLen);
} else {
let newValue = $event.target.value;
if (newValue && this.maxLen > -1 && newValue.length > this.maxLen) {
newValue = newValue.substring(0, this.maxLen);
$event.target.value = newValue;
}
}
}
// angular 代码;就是paste事件,其他框架通用这个方法
@HostListener('paste', ['$event'])
onPaste(event: ClipboardEvent) {
// 一个检测事件,监测限制值是否为负数
if (!this.isTriggerKeyEvents()) {
return;
}
// 禁止输入
event.preventDefault();
let currentVal = this.val;
if (this.maxLen > -1 || this.numberOnly) {
// 获取粘贴的值
let clipboardData = event.clipboardData.getData('text/plain');
if (this.numberOnly) {
let pastedInput: string = clipboardData.replace(/\D/g, ''); // get a number-only string
this.val = currentVal + pastedInput;
}
if (this.maxLen > -1) {
if (this.CN) {
// 限制的汉字
this.val = this.limitPaste(currentVal, this.maxLen, clipboardData);
this.calculateCurrentLen();
} else {
let pastedInput: string = clipboardData.substring(0, this.maxLen);
this.val = currentVal + pastedInput;
}
}
}
}
private limitPaste(value: string, byteLength: number, pastedVal: string) {
if (!pastedVal) {
return
}
if (!value) {
let pastedValue = pastedVal.replace(/[^\x00-\xff]/g, "**");
let limitDate = pastedValue.substr(0, byteLength);
return this.limtFunc(limitDate, byteLength, pastedVal)
}
let newvalue = value.replace(/[^\x00-\xff]/g, "**");
let length = newvalue.length;//获取内容长度
let split_value = value + pastedVal;
if (length * 1 >= byteLength * 1) {
let limitDate = newvalue.substr(0, byteLength);
return this.limtFunc(limitDate, byteLength, value)
}
if (length * 1 < byteLength * 1) {
let pastedValue = pastedVal.replace(/[^\x00-\xff]/g, "**");
let val = newvalue + pastedValue;
let limitDate = val.substr(0, byteLength);
return this.limtFunc(limitDate, byteLength, split_value)
}
}
// typeScript,js时不需要参数后面的类型
limitLength(value: string, byteLength: number) {
let newvalue = value.replace(/[^\x00-\xff]/g, "**"); //输入内容
let length = newvalue.length;//获取内容长度
//当填写的字节数小于设置的字节数
if (length * 1 <= byteLength * 1) {
return value;
}
let limitDate = newvalue.substr(0, byteLength);
let count = 0;
let limitvalue = "";
for (let i = 0; i < limitDate.length; i++) {
let flat = limitDate.substr(i, 1);
if (flat == "*") {
count++;
}
}
let size = 0;
//if 基点是×; 判断在基点内有×为偶数还是奇数
if (count % 2 == 0) {
//当为偶数时
size = count / 2 + (byteLength * 1 - count);
limitvalue = value.substr(0, size);
} else {
//当为奇数时
size = (count - 1) / 2 + (byteLength * 1 - count);
limitvalue = value.substr(0, size);
}
return limitvalue;
}
由于我的是angular的代码,因此使用了ts而非js,但是看起来各位大佬一定觉得很 easy,毕竟我这个只是总结前人经验应用到我这里而已
我这边将input封装为组件,支持限制字符、汉字、粘贴、仅输入数字功能
html:
<div class="cil-input-wrapper flex-wrap row-flex">
<div class="flex-wrap col-flex" *ngIf="label">
<label for="cil-input" class="cil-input-lbl">
<span class="form-required" *ngIf="isRequired">*span>
{{ label }}
label>
div>
<div class="flex-wrap col-flex input-container">
<input id="cil-input" class="cil-input" type="text" [(ngModel)]="val" [placeholder]="placeholder" (input)="onInput($event)"
(compositionstart)="onCompositionstart()" (compositionend)="onCompositionEnd($event)" (change)="onChange($event)"
(keyup)="onChange($event)" (blur)="onBlur($event)" [attr.disabled]="isDisabled ? '' : null"
[class.disabled]="isDisabled" [style.padding-right.px]="(maxLen > -1 && maxLen < 1000) ? '65' : '10'"
autocomplete="off">
<span class="error-message" *ngIf="errorMessage">{{ errorMessage }}span>
<div class="cil-len" *ngIf="maxLen > -1 && maxLen < 1000">
<span class="cur-length">{{ currentLen }}span> / <span>{{ maxLen }}span>
div>
div>
div>
import { Component, ViewEncapsulation, ChangeDetectionStrategy, Input, OnInit, forwardRef, ChangeDetectorRef, OnChanges, SimpleChanges, HostListener } from "@angular/core";
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlValueAccessor, Validator, ValidationErrors, AbstractControl } from "@angular/forms";
@Component({
selector: `cil-input`,
templateUrl: 'input.html',
styleUrls: ['input.scss'],
encapsulation: ViewEncapsulation.None,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CilInput),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => CilInput),
multi: true,
}]
})
export class CilInput implements OnInit, OnChanges, ControlValueAccessor, Validator {
val: string;
private navigationKeys = [
'Backspace',
'Delete',
'Tab',
'Escape',
'Enter',
'Home',
'End',
'ArrowLeft',
'ArrowRight',
'Clear',
'Copy',
'Paste'
];
currentLen: number = 0;
chineseReg = /[^\x00-\xff]/g;
// 输入完成的标记
isComposite: boolean = false;
@Input() label: string;
@Input() isRequired: boolean;
@Input() placeholder: string = '';
@Input() errorMessage: string;
@Input() isDisabled: boolean;
@Input() CN: boolean = false;
// Default -1 indicate that the length of input is not limited
// The max of maxLen is 999
@Input() maxLen: number = -1;
@Input() numberOnly: boolean = false;
private emitChange = (_: any) => { };
constructor() { }
ngOnInit() { }
ngOnChanges(changes: SimpleChanges): void {
if (changes) { }
}
onChange(event: any) {
this.onBlur(event);
}
onBlur(event: any) {
// this.onInput(event);
this.emitChange(this.val);
}
writeValue(obj: any): void {
if (obj || obj === '') {
this.val = obj;
if (this.maxLen > -1) {
this.calculateCurrentLen();
}
}
}
registerOnChange(fn: any): void {
this.emitChange = fn;
}
registerOnTouched(fn: any): void { }
validate(control: AbstractControl): ValidationErrors {
// TODO: customize validation
return null;
}
@HostListener('keyup', ['$event'])
onKeyup(e: KeyboardEvent) {
// console.log('>>>>>>')
if (this.maxLen === -1) {
return;
}
if (this.maxLen > -1) {
this.calculateCurrentLen();
// Hack: when typing chinese character, input control will not prevent the character typing
// let chineseCharLen = this.val ? (this.val.match(this.chineseReg) || []) : [];
// if (chineseCharLen.length > 0) {
// let len = this.maxLen - chineseCharLen.length + 1;
// this.val = this.val.substring(0, len);
// }
}
if (this.numberOnly) {
this.val = this.val ? this.val.replace(this.chineseReg, '') : '';
}
}
@HostListener('keydown', ['$event'])
onKeyDown(e: KeyboardEvent) {
if (!this.isTriggerKeyEvents()) {
return;
}
if (
this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
(e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
(e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
(e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
(e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
(e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
(e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
(e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
(e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
) {
return;
}
if (this.maxLen > -1) {
let hightlightText = window.getSelection() + '';
let currentLen = 0;
if (this.CN) {
let chineseCharLen = this.val ? (this.val.match(this.chineseReg) || []) : [];
currentLen = this.val ? this.val.length + chineseCharLen.length : 0;
} else {
currentLen = this.val ? this.val.length : 0;
}
if (this.val && currentLen >= this.maxLen && hightlightText === '') {
e.preventDefault();
}
}
if (this.numberOnly) {
if (isNaN(Number(e.key))) {
e.preventDefault();
}
}
}
// @HostListener('compositionstart')
onCompositionstart() {
this.isComposite = true;
}
// @HostListener('compositionEnd', ['$event'])
onCompositionEnd($event) {
this.isComposite = false;
this.onInput($event);
}
// @HostListener('input', ['$event'])
onInput($event) {
if (!$event || this.isComposite) {
return;
}
const target = $event.target;
if (this.CN) {
target.value = this.limitLength(target.value, this.maxLen);
this.val = this.limitLength(target.value, this.maxLen);
} else {
let newValue = $event.target.value;
if (newValue && this.maxLen > -1 && newValue.length > this.maxLen) {
newValue = newValue.substring(0, this.maxLen);
$event.target.value = newValue;
}
}
}
limitLength(value: string, byteLength: number) {
let newvalue = value.replace(/[^\x00-\xff]/g, "**"); //输入内容
let length = newvalue.length;//获取内容长度
//当填写的字节数小于设置的字节数
if (length * 1 <= byteLength * 1) {
return value;
}
let limitDate = newvalue.substr(0, byteLength);
let count = 0;
let limitvalue = "";
for (let i = 0; i < limitDate.length; i++) {
let flat = limitDate.substr(i, 1);
if (flat == "*") {
count++;
}
}
let size = 0;
//if 基点是×; 判断在基点内有×为偶数还是奇数
if (count % 2 == 0) {
//当为偶数时
size = count / 2 + (byteLength * 1 - count);
limitvalue = value.substr(0, size);
} else {
//当为奇数时
size = (count - 1) / 2 + (byteLength * 1 - count);
limitvalue = value.substr(0, size);
}
return limitvalue;
}
@HostListener('paste', ['$event'])
onPaste(event: ClipboardEvent) {
if (!this.isTriggerKeyEvents()) {
return;
}
event.preventDefault();
let currentVal = this.val;
if (this.maxLen > -1 || this.numberOnly) {
let clipboardData = event.clipboardData.getData('text/plain');
if (this.numberOnly) {
let pastedInput: string = clipboardData.replace(/\D/g, ''); // get a number-only string
this.val = currentVal + pastedInput;
}
if (this.maxLen > -1) {
if (this.CN) {
this.val = this.limitPaste(currentVal, this.maxLen, clipboardData);
this.calculateCurrentLen();
} else {
let pastedInput: string = clipboardData.substring(0, this.maxLen);
this.val = currentVal + pastedInput;
}
}
}
}
private limitPaste(value: string, byteLength: number, pastedVal: string) {
if (!pastedVal) {
return
}
if (!value) {
let pastedValue = pastedVal.replace(/[^\x00-\xff]/g, "**");
let limitDate = pastedValue.substr(0, byteLength);
return this.limtFunc(limitDate, byteLength, pastedVal)
}
let newvalue = value.replace(/[^\x00-\xff]/g, "**");
let length = newvalue.length;//获取内容长度
let split_value = value + pastedVal;
if (length * 1 >= byteLength * 1) {
let limitDate = newvalue.substr(0, byteLength);
return this.limtFunc(limitDate, byteLength, value)
}
if (length * 1 < byteLength * 1) {
let pastedValue = pastedVal.replace(/[^\x00-\xff]/g, "**");
let val = newvalue + pastedValue;
let limitDate = val.substr(0, byteLength);
return this.limtFunc(limitDate, byteLength, split_value)
}
}
private limtFunc(limitVal: string, byteLength: number, split_value: string) {
let count = 0;
let limitvalue = "";
for (let i = 0; i < limitVal.length; i++) {
let flat = limitVal.substr(i, 1);
if (flat == "*") {
count++;
}
}
let size = 0;
//if 基点是×; 判断在基点内有×为偶数还是奇数
if (count % 2 == 0) {
//当为偶数时
size = count / 2 + (byteLength * 1 - count);
limitvalue = split_value.substr(0, size);
} else {
//当为奇数时
size = (count - 1) / 2 + (byteLength * 1 - count);
limitvalue = split_value.substr(0, size);
}
return limitvalue
}
private calculateCurrentLen() {
if (this.CN) {
let chineseCharLen = this.val ? (this.val.match(this.chineseReg) || []) : [];
this.currentLen = this.val ? this.val.length + chineseCharLen.length : 0;
} else {
this.currentLen = this.val ? this.val.length : 0;
}
this.currentLen = this.currentLen > this.maxLen ? this.maxLen : this.currentLen;
}
private isTriggerKeyEvents() {
return this.maxLen !== -1 || this.numberOnly;
}
}
此时在项目中应用就很简单
<cil-input formControlName="testName" [isRequired]="true" [errorMessage]="requiredMessage"
[placeholder]="'请输入您的品牌名称'" [maxLen]="10" [CN]="true">cil-input>
<span style="padding: 10px 0 10px 100px;">输入内容: {{f.testName.value}}span>
<br>
<cil-input [label]="'汉语字符'" [(ngModel)]="inputValCN" [isRequired]="true" [placeholder]="'请输入您的商户名称'" [maxLen]="10" [CN]="true">cil-input>
当有属性maxLen时候进行限制,默认对中文不做任何校验,当传递CN为true的时候则对中文进行限制;
学海无涯,回头是岸