当前位置:   article > 正文

input组件支持限制字符、汉字_在angularjs中,input描述用于显示输入框中的字数限制类似show-word-limit

在angularjs中,input描述用于显示输入框中的字数限制类似show-word-limit

在平时开发的过程中,产品终会提一些另辟蹊径的建议,例如我们在做输入限制的时候,产品希望可以根据输入字符汉字不同去做不同限制;例如:限制输入10个字符或者5个汉字

开发时候踩到的坑

  1. 中文截断问题
    这个问题应该很多人都碰到过,并且网上的解决方法也有很多,其实使用onkeydown/onkeypress/onkeyup事件来监听并不能解决复制问题,在苦(cha)思(zhao)冥(gu)想(ge)的情况下,让我想(zhao)到了一种方法:compositionstart和compositionend+onInput
    compositionstart 事件在用户开始进行非直接输入的时候触发,而在非直接输入结束,也即用户点选候选词或者点击「选定」按钮之后,会触发 compositionend 事件。
// 嫌弃代码不全的队友请放心,下面会有完整的代码
// @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;
      }
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  1. 复制问题
    这个问题解决起来相当绕,因为在输入限制的时候复制粘贴的时候需要考虑到是否有原有内容,如果有,则限制的时候需要考虑到需要加上已有的内容,因此使用了paste监控粘贴
// 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)
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  1. 如何区分中文情况下一个汉字顶两个字符
    标题很浅显,就是去判断这个是个汉字,然后如果是汉字记录一下,在超出限制的时候方便进行删除截取
// 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;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

完整代码

由于我的是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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
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;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311

应用

此时在项目中应用就很简单

<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>
  • 1
  • 2
  • 3
  • 4
  • 5

当有属性maxLen时候进行限制,默认对中文不做任何校验,当传递CN为true的时候则对中文进行限制;

总结

学海无涯,回头是岸

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/223024
推荐阅读
相关标签
  

闽ICP备14008679号