繁体   English   中英

如何使用 ControlValueAccessor Angular 在自定义输入中使用指令

[英]How To Use A Directive In A Custom Input With ControlValueAccessor Angular

我已经在我的 angular 应用程序中使用ControlValueAccessor创建了一个简单的自定义input组件。 因此,当我想创建表单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, ''));
    }
}

有没有办法在my-input组件中使用myDirective

提前致谢。

你的指令有问题。 注入一个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 { }

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

根据 Angular Directives 文档:

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

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

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

请记住,您必须定义特定指令,这些指令仅适用于特定查询,不能用于任何 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 带红色 bg]

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

我们的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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM