更新指令中的属性值

问题描述 投票:3回答:3

我正在尝试创建自己的指令来计算输入长度值。

  • 在视图init上我用我的指令添加了maxlength属性值,发送到指令 - > ok
  • 在查看init之后,我在我的指令之前添加一个div,其计数如0/50 - > ok

我在使用键盘时更新长度值时遇到问题(属性已更新,但未更新)。你能帮我吗?

import {
  AfterViewInit,
  Directive, ElementRef, HostListener, Input, OnInit, Renderer2
} from '@angular/core';

@Directive({
  selector: '[appInputmaxLength]'
})
export class InputmaxLengthDirective implements OnInit, AfterViewInit {
  @Input() appInputmaxLength: string;
  private currentValue = 0;

  constructor(
    private el: ElementRef,
    private renderer: Renderer2
  ) {}

  @HostListener('keydown') isChange() {
    let countNb = this.el.nativeElement.value.length + 1;
    if (countNb <= 1) {
      this.currentValue = 0;
    } else {
      this.currentValue = countNb;
    }

    console.log('test: ', this.el.nativeElement.value.length + 1);
  }

  ngOnInit() {
    this.renderer.setAttribute(this.el.nativeElement, 'maxLength', this.appInputmaxLength);
  }

  ngAfterViewInit() {
    const html = '<div>' + this.currentValue + ' / ' + this.appInputmaxLength + '</div>'
    const target = this.el;
    target.nativeElement.insertAdjacentHTML('afterEnd', html);
  }

}

这就是我使用指令的方式:

<input type="text" [appInputmaxLength]="'5'" />

谢谢你的帮助,我迷路了。

angular angular2-directives
3个回答
1
投票

该指令的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引用将为null。你必须直接打电话给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">

0
投票

正确的方法是使用二传手。每次输入更改时都会调用setter函数。

    @Input() set appInputmaxLength(value:string){
    // Your code here
    console.log(value);
    }

可以在这里找到一个例子:https://angular.io/guide/structural-directives(你的指令不是结构指令,但是在这个例子中)


0
投票

尝试这个代码我已经重写了一些代码

import {
  AfterViewInit,
  Directive,
  ElementRef,
  HostListener,
  Input,
  OnInit,
  Renderer2,
  OnDestroy
} from '@angular/core';

@Directive({
  selector: '[appInputmaxLength]'
})
export class InputmaxLengthDirective implements OnInit, AfterViewInit, OnDestroy {
  @Input() appInputmaxLength: string;
  private currentValue = 0;
  countDiv: HTMLDivElement;
  parent: any;

  constructor(private el: ElementRef<HTMLInputElement>, private renderer: Renderer2) {}

  @HostListener('keyup') isChange() {
    const countNb = this.el.nativeElement.value.length + 1;
    if (countNb <= 1) {
      this.currentValue = 0;
      this.updateCount();
    } else {
      this.currentValue = countNb;
      this.updateCount();
    }

    console.log('test: ', this.el.nativeElement.value.length + 1);
  }

  ngOnInit() {
    this.renderer.setAttribute(this.el.nativeElement, 'maxLength', this.appInputmaxLength);
  }

  ngOnDestroy() {
    this.renderer.removeChild(this.parent, this.countDiv);
    this.renderer.destroyNode(this.countDiv);
  }

  updateCount() {
    this.countDiv.innerText = this.currentValue + ' / ' + this.appInputmaxLength;
  }

  ngAfterViewInit() {
    this.countDiv = this.renderer.createElement('div');
    this.parent = this.renderer.parentNode(this.el.nativeElement);
    this.renderer.appendChild(parent, this.countDiv);
    this.updateCount();
  }
}

在这段代码中,我使用渲染器来创建一个div元素,并在currentValue发生变化时更新其值。

当指令被销毁时,你必须销毁它以避免内存泄漏

© www.soinside.com 2019 - 2024. All rights reserved.