6

我已经input在我的 Angular 应用程序使用中创建了一个简单的自定义组件ControlValueAccessor。所以,当我想创建一个表单input元素时,我不必调用<input />,只需调用<my-input>

我有一个问题,当我使用时<input />,我可以使用myDirective. 例如:

<input type="text" class="form-control" formControlName="name" myDirective />

但是,当我使用 时my-input,我无法使用myDirective. 例如:

<my-input formControlName="name" myDirective></my-input>

myDirective在我的输入中不起作用

这是my-input组件使用ControlValueAccessor代码:

import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

@Component({
  selector: 'my-input',
  templateUrl: './my-input.component.html',
  styleUrls: ['./my-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(()  => MyInputComponent ),
      multi: true
    }
  ]
})

export class MyInputComponent implements ControlValueAccessor {

  onChange: () => void;
  onTouched: () => void;

  value: string;

  writeValue(value: string): void {
    this.value = value ? value : '';
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
}

更新: myDirective代码:

import { Directive, HostListener } from '@angular/core';
import { FormControlName } from '@angular/forms';

@Directive({
    selector: '[myDirective]'
})

export class MyDirective{
    constructor(private formControlName: FormControlName) { }

    @HostListener('input', ['$event']) 
    onInputChange() {
        this.formControlName.control.setValue(this.formControlName.value.replace(/[^0-9]/g, ''));
    }
}

有没有办法myDirectivemy-input组件中使用?

提前致谢。

4

2 回答 2

4

你的指令有问题。注入一个NgControl并控制这个 ngControl

export class MyDirective{
    constructor(private control: NgControl) { } //<--inject NgControl

    @HostListener('input', ['$event']) 
    onInputChange() {
        this.control.control.setValue(this.control.value.replace(/[^0-9]/g, ''));
    }
}

你可以在stackblitz中看到

注意:不要忘记包含在模块声明中

@NgModule({
  imports:      [ BrowserModule, FormsModule,ReactiveFormsModule ],
  declarations: [ AppComponent, MyInputComponent,MyDirective ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
于 2020-02-19T09:40:28.307 回答
2

你不能直接,但你可以用指令查询做一些 Hack

根据 Angular Directives 文档:

将类标记为 Angular 指令的装饰器。您可以定义自己的指令来将自定义行为附加到 DOM 中的元素。

装饰器是附加到 DOM 元素的行为,因此它会影响使用该指令的元素。

但是(并且是一个很大的原因 - 检查说明),您可以使用指令查询来破解此行为。

请记住,您必须定义仅适用于特定查询且不能用于任何 DOM 元素的特定指令。

解决方案

该解决方案基于以下几点:

我想定义一个适用于元素子元素的指令,而不是元素本身。

您可以使用@ContentChildren和 QueryList 来检查您是否有一些具有特定指令的孩子。

例如,我有一个背景指令应用于输入父级和一个CustomFormControlDirective来查询子级:

import {
  Directive,
  ElementRef,
  Input,
  ContentChildren,
  ViewChildren,
  QueryList
} from "@angular/core";

@Directive({
  selector: "[customformcontrol]"
})
export class CustomFormControlDirective {
  constructor(public elementRef: ElementRef) {}
}

@Directive({
  selector: "[backgroundColor]",
  queries: {
    contentChildren: new ContentChildren(CustomFormControlDirective),
    viewChildren: new ViewChildren(CustomFormControlDirective)
  }
})
export class BackgroundColorDirective {
  @Input()
  set backgroundColor(color: string) {
    this.elementRef.nativeElement.style.backgroundColor = color;
  }

  @ContentChildren(CustomFormControlDirective, { descendants: true })
  contentChildren: QueryList<CustomFormControlDirective>;
  viewChildren: QueryList<CustomFormControlDirective>;

  constructor(private elementRef: ElementRef) {}

  ngAfterContentInit() {
    // contentChildren is set
    console.log("%o", this.contentChildren);
  }
}
[...]
<div backgroundColor="red">
  <input customformcontrol />
</div>

当然,这个指令会将 bg 颜色应用于父 div:

带红色背景的 div]

那么我们如何将这个属性设置给孩子呢?

我们的contentChildren变量中有孩子:

控制台日志

所以我们需要为子元素应用所需的背景,我们可以遍历查询结果并尝试应用样式:

  [...]
  _backgroundColor = null;
  @Input()
  set backgroundColor(color: string) {
    /// save the bg color instead of apply style
    this._backgroundColor = color;
  }
  ngAfterContentInit() {
    if (this.contentChildren.length) {
      /// then loop through childrens to apply style
      this.contentChildren.forEach(customFormControl => {
        customFormControl.elementRef.nativeElement.style.backgroundColor = this._backgroundColor;
      });
    }
  }
  [...]

它会将样式应用于儿童: 有红色 bg 的儿童

如果有超过 1 个孩子: 更多花哨的红色bgs

笔记

  • 这是一个示例,不要按原样采用此实现,您可以定义自己的方法或使用更好的方法,但尝试了解 QueryList 选择器和 ContentChildren。
  • 可能不需要使用自定义指令来获取孩子,您可以直接使用 ReactiveForm 指令(AbstractControlDirective / FormControlDirective),但我认为它们不会让您访问 DOM,因为它是私有的。
  • 这些指令仅适用于儿童,因此您可以选择更好的命名约定(例如 ApplyToControlBackgroundDirective)
于 2020-02-19T09:33:47.240 回答