Вот демонстрация Stackblitz директивы
Я внес несколько изменений в ваш код, вот что я предлагаю:
- Измените тип
appInputMaxLength
на число - Используйте API
Renderer2
столько, сколько вы можете быть кросс-платформенным. - Используйте частное свойство
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)
, так как ngOnDestroy
вызывается после удаления DOM, и ссылка parent
будет нулевой.Вы должны напрямую позвонить this.div.remove()
( см. Эту проблему GitHub ).
Обновленный код директивы
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
наблюдаемый:
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">