Angular 指令:自定义属性未正确解析

Angular Directive: Custom attribute not properly resolving

我正在使用 ng-bootstrap 为表单中的一堆输入字段显示错误工具提示。 由于大部分代码是重复的,我考虑创建一个指令来根据一些参数将属性设置为元素:

来自这里:

<div class="value" tooltipClass="tooltip-error"
     [ngbTooltip]="'getError(form, 'serial')'"
     [openDelay]="300" [closeDelay]="500"
     [ngClass]="{ 'is-invalid': submitted && form.get('serial').errors }">
  <input formControlName="serial" type="text" placeholder="Serial Number" />
</div>

为此:

<div class="value" [form]="form" [submitted]="submitted" [controlName]="'serial'">
  <input formControlName="serial" type="text" placeholder="Serial Number" />
</div>

使用这个指令:

import { Directive, Input, HostBinding } from '@angular/core';
import { FormGroup } from '@angular/forms';

import { formError } from '@core/helpers';

@Directive({
  selector: '[submitted][form][controlName],[submitted][form][controlName][disabled]',
  host: {
    'tooltipClass': 'tooltip-error',
    '[attr.openDelay]': '300',
    '[attr.closeDelay]': '500',
    '[class.is-invalid]': 'submitted && form.get(controlName).errors',
    '[class.not-allowed-cursor]': 'disabled',
    '[class.no-text-selection]': 'disabled',
  }
})
export class FormControlDirective {
  _controlName: string = '';
  _form!: FormGroup;
  _submitted!: boolean;

  @Input()
  public get submitted(): boolean {
    return this._submitted;
  }
  public set submitted(v : boolean) {
    this._submitted = v;
    this.resolveTooltip();
  }

  @Input()
  public get form(): FormGroup {
    return this._form;
  }
  public set form(v : FormGroup) {
    this._form = v;
    this.resolveTooltip();
  }

  @Input()
  public get controlName(): string {
    return this._controlName;
  }
  public set controlName(v : string) {
    this._controlName = v;
    this.resolveTooltip();
  }

  @Input() disabled: boolean = false;

  @HostBinding('[attr.ngbTooltip]')
  ngbTooltip: string = '';

  resolveTooltip() {
    //This method gets an error message based on the type of error.
    //I tried to put the value between ' ', with no avail.
    this.ngbTooltip = formError(this.submitted, this.form, this.controlName);
  }
}

首先,我注意到 HostBinding 中的值不会自动更新,就像 host 属性一样,所以我创建了 getters/setter.

但是,工具提示仍然没有出现。如果我手动添加属性,工具提示会正常显示。

手动方式渲染结果如下:

<div tooltipclass="tooltip-error" class="value is-invalid" ng-reflect-tooltip-class="tooltip-error" ng-reflect-ngb-tooltip="Error example" ng-reflect-open-delay="300" ng-reflect-close-delay="500" ng-reflect-ng-class="[object Object]">
  <input formcontrolname="serial" type="text" placeholder="Serial Number" ng-reflect-name="serial" class="ng-dirty ng-invalid ng-touched">
</div>

这是使用指令的渲染结果:

<div tooltipclass="tooltip-error" class="value is-invalid" ng-reflect-form="[object Object]" ng-reflect-submitted="true" ng-reflect-control-name="serial" opendelay="300" closedelay="500" ngbtooltip="'Serial number is required'">
  <input formcontrolname="serial" type="text" placeholder="Serial Number" ng-reflect-name="serial" class="ng-dirty ng-invalid ng-touched">
</div>

据我所知,属性似乎没有被“解析”,没有显示为 ng-reflect-

尝试简单地添加以 ng-reflect- 开头的属性名称是行不通的。 :)

这是怎么回事,如何解决?


编辑:

我想我明白发生了什么。 我试图在我的自定义指令中触发另一个指令的选择器([ngbTooltip],来自 ng-boostrap)。

如果我创建更简单的东西,像这样:

'[attr.ngbTooltip]': 'tooltipText',
//...

tooltipText: string = '';
//...

resolveTooltip() {
  this.tooltipText = formError(this.submitted, this.form, this.controlName);
}

我将获得一个名为 ngbTooltip="" 而不是 [ngbTooltip]="" 的属性,它是 ng-bootstrapngbTooltip 的触发器。

有什么方法可以从自定义指令中触发另一个指令的选择器吗?


答案?

这看起来确实是错误的,但它确实有效。 今天实在想不下去了

我所做的是从 ngbTooltip 扩展并将值设置为工具提示使用的内部属性。

import { Directive, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';

import { formError } from '@core/helpers';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';

@Directive({
  selector: '[submitted]',
  host: {
    '[class.is-invalid]': 'submitted && form.get(controlName).errors',
    '[class.not-allowed-cursor]': 'disabled',
    '[class.no-text-selection]': 'disabled'
  }
})
export class FormControlDirective extends NgbTooltip implements OnInit {
  _controlName: string = '';
  _form!: FormGroup;
  _submitted: boolean = false;

  @Input()
  public get submitted(): boolean {
    return this._submitted;
  }
  public set submitted(v : boolean) {
    this._submitted = v;
    this.resolveTooltip();
  }

  @Input()
  public get form(): FormGroup {
    return this._form;
  }
  public set form(v : FormGroup) {
    this._form = v;
    this.resolveTooltip();
  }

  @Input()
  public get controlName(): string {
    return this._controlName;
  }
  public set controlName(v : string) {
    this._controlName = v;
    this.resolveTooltip();
  }

  @Input() disabled: boolean = false;

  ngOnInit(): void {
    this.tooltipClass = 'tooltip-error';
    this.openDelay = 300;
    this.closeDelay = 500;

    super.ngOnInit();
  }

  resolveTooltip() {
    this.ngbTooltip = formError(this.submitted, this.form, this.controlName);
  }
}

selector 是您应用指令的方式 - 一个简单的关键字就足够了。

在您的情况下,您最感兴趣的是主机 class 属性,这是可以理解的,这部分看起来是正确的。

您可以先修复选择器部分,然后尝试 re-apply 您的指令。

这是工具提示指令的示例:https://stackblitz.com/edit/angular-tooltip-directive-editable-egzs44?file=app%2Ftooltip.directive.ts

我认为您的 HostBinding 语法可能不正确

@HostBinding('[attr.ngbTooltip]')
  ngbTooltip: string = '';

应该是:

@HostBinding('attr.ngbTooltip')
  ngbTooltip: string = '';

此外,据我所知,指令选择器 名称只能用于单个 @Input,但您将其用于多个输入,例如 [submitted]="submitted" [controlName]="'serial'" - 我认为这行不通

Tim Deschryver 在此处使用 PrimeNg 指令解决了类似的用例:

https://dev.to/this-is-angular/use-angular-directives-to-extend-components-that-you-dont-own-1jio

在我看来,仅通过自定义指令应用指令的属性并不是一个好方法,因为您会创建强大的 link,但我并不是说这行不通完全没有。

Tim 解决方案看起来不错,但您需要映射 ng-bootstrap 现有指令,而不是您映射的指令。

你扩展 class 的解决方案看起来像你说的那样“边缘”;) 干杯!