Here is a Stackblitz demo 的指令
我对你的代码做了一些修改,这是我的建议:
- 将您的
appInputMaxLength 类型更改为数字
- 尽可能多地使用
Renderer2 API,以实现跨平台兼容。
- 使用私有
div 属性来保存您的 div 并稍后更新它,使用 this.renderer.createElement('div') 创建它
- 使用
this.renderer.insertBefore(this.el.nativeElement.parentNode, this.div, this.el.nativeElement.nextSibling)在主机后面插入
- 使用
input事件监听变化,并从事件中获取值,然后获取其长度并更新div
- 您不需要保留
currentValue 变量,只需从输入的值或事件中获取长度即可
- 使用
this.renderer.setProperty(this.div, 'innerText', ...); 更新 div 元素的文本
- 删除 div 元素,因为 Angular 不会跟踪它。为此,您不能使用
this.renderer.removeChild(this.el.nativeElement.parent, this.div),因为在删除 DOM 后会调用 ngOnDestroy,并且 parent 引用将为空。您必须直接致电this.div.remove() (see this github issue)。
更新指令代码
import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnInit, Renderer2, OnDestroy } from '@angular/core';
@Directive({
selector: '[appInputMaxLength]'
})
export class InputMaxLengthDirective implements OnInit, AfterViewInit, OnDestroy {
@Input() appInputMaxLength: number;
private div: HTMLDivElement;
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('input', ['$event']) onChange(event) {
this.update(event.target.value.length);
}
ngOnInit() {
this.renderer.setAttribute(this.el.nativeElement, 'maxLength', this.appInputMaxLength.toString());
}
ngOnDestroy() {
if (this.div) {
this.div.remove();
}
}
ngAfterViewInit() {
this.div = this.renderer.createElement('div');
this.renderer.insertBefore(this.el.nativeElement.parentNode, this.div, this.el.nativeElement.nextSibling);
this.update(this.el.nativeElement.value.length);
}
private update(length: number) {
this.renderer.setProperty(this.div, 'innerText', `${length} / ${this.appInputMaxLength}`);
}
}
像这样使用它,带有一个数字输入值:
<input type="text" [appInputMaxLength]="10">
与ngModel兼容的指令代码
如果您希望指令在 ngModel 绑定到输入时起作用,并在模型更改时相应更新,您可以通过注入主机 ngModel 然后订阅其 valueChange observable:
import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnInit, Renderer2, Optional, OnDestroy } from '@angular/core';
import { NgModel } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Directive({
selector: '[appInputMaxLength]'
})
export class InputMaxLengthDirective implements OnInit, AfterViewInit, OnDestroy {
@Input() appInputMaxLength: number;
private div: HTMLDivElement;
private destroyed$ = new Subject();
constructor(private el: ElementRef, private renderer: Renderer2, @Optional() private ngModel: NgModel) {}
@HostListener('input', ['$event']) onChange(event) {
if (!this.ngModel) {
this.update(event.target.value.length);
}
}
ngOnInit() {
this.renderer.setAttribute(this.el.nativeElement, 'maxLength', this.appInputMaxLength.toString());
if (this.ngModel) {
this.ngModel.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(value => {
this.update(value.length);
})
}
}
ngAfterViewInit() {
this.div = this.renderer.createElement('div');
this.renderer.insertBefore(this.el.nativeElement.parentNode, this.div, this.el.nativeElement.nextSibling);
this.update(this.el.nativeElement.value.length);
}
ngOnDestroy() {
this.destroyed$.next();
this.destroyed$.complete();
if (this.div) {
this.div.remove();
}
}
private update(length: number) {
this.renderer.setProperty(this.div, 'innerText', `${length} / ${this.appInputMaxLength}`);
}
}
然后您可以在带有ngModel 的输入上使用您的指令:
<input type="text" [appInputMaxLength]="10" [(ngModel)]="value">