简体   繁体   English

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

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

I've been create a simple custom input component in my angular app use ControlValueAccessor .我已经在我的 angular 应用程序中使用ControlValueAccessor创建了一个简单的自定义input组件。 So, when I want to create a form input element, I don't have to call <input /> , only call <my-input> .因此,当我想创建表单input元素时,不必调用<input /> ,只需调用<my-input>

I have a problem, when I use <input /> , I can use myDirective .我有一个问题,当我使用<input /> ,我可以使用myDirective For example:例如:

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

But, when I use my-input , then I can't use myDirective .但是,当我使用my-input ,我不能使用myDirective For example:例如:

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

myDirective dosn't work in my-input myDirective在我的输入中不起作用

This is my-input component use ControlValueAccessor code:这是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;
  }
}

Updated: myDirective code:更新: 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, ''));
    }
}

Is there a way for myDirective to be used in the my-input component?有没有办法在my-input组件中使用myDirective

Thanks in advance.提前致谢。

There're a problem with your directive.你的指令有问题。 Inject a NgControl and control this ngControl注入一个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, ''));
    }
}

You can see in stackblitz你可以在stackblitz中看到

NOTE: Don't forget include in the module declarations注意:不要忘记包含在模块声明中

@NgModule({
  imports:      [ BrowserModule, FormsModule,ReactiveFormsModule ],
  declarations: [ AppComponent, MyInputComponent,MyDirective ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

You can't directly but you can do some Hack with directives queries你不能直接但你可以用指令查询做一些 Hack

According to the Angular Directives documentation:根据 Angular Directives 文档:

Decorator that marks a class as an Angular directive.将类标记为 Angular 指令的装饰器。 You can define your own directives to attach custom behaviour to elements in the DOM.您可以定义自己的指令来将自定义行为附加到 DOM 中的元素。

a decorator is a behaviour attached to an element to the DOM, so it will affect the element wich use the directive.装饰器是附加到 DOM 元素的行为,因此它会影响使用该指令的元素。

BUT (and is a big BUT for a reason - check notes), you can hack this behaviour using directive queries.但是(并且是一个很大的 BUT 是有原因的 - 检查注释),您可以使用指令查询来破解此行为。

BUT remember you must define specific directives which will work only with specific queries and cannot be used for any DOM element.请记住,您必须定义特定指令,这些指令仅适用于特定查询,不能用于任何 DOM 元素。

The solution解决方案

The solution is based on the following:解决方案基于以下几点:

I want to define a directive which works on an element children, and not on the element itself.我想定义一个指令,它适用于元素子元素,而不是元素本身。

You can use @ContentChildren and QueryList to check if you have some children with a specific directive.您可以使用@ContentChildren和 QueryList 来检查您是否有一些具有特定指令的孩子。

For example, I have a background directive applied to input parent and a CustomFormControlDirective to query the children:例如,我有一个后台指令应用于输入父级和一个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>

Of course this directive will apply the bg color to the parent div:当然,该指令会将 bg 颜色应用于父 div:

div 带红色 bg]

So how can we set this property to the children?那么我们如何将这个属性设置给孩子呢?

We have the children inside our contentChildren variable:我们的contentChildren变量中有孩子:

控制台日志

So we need to apply the desired background to our children element, we can loop trough the query results and try to apply the style:所以我们需要将所需的背景应用到我们的子元素,我们可以遍历查询结果并尝试应用样式:

  [...]
  _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;
      });
    }
  }
  [...]

And it will apply the style to childrens:它会将样式应用于儿童: 有红色 bg 的孩子

also if there are more than 1 children:如果有超过 1 个孩子: 更多花哨的红色bgs

Notes笔记

  • this is an example, don't take this implementation as-is, you might define your own method or use a better one, but try to understand the QueryList selectors and ContentChildren.这是一个示例,不要按原样使用此实现,您可以定义自己的方法或使用更好的方法,但请尝试了解 QueryList 选择器和 ContentChildren。
  • using a custom directive to fetch childrens might not be necessary, you might use ReactiveForm directives directly (AbstractControlDirective / FormControlDirective) but I don't think they will let you access the DOM as it is private.可能不需要使用自定义指令来获取儿童,您可以直接使用 ReactiveForm 指令(AbstractControlDirective / FormControlDirective),但我认为它们不会让您访问 DOM,因为它是私有的。
  • these directives will work only with children, so you might choose a better naming convention (eg ApplyToControlBackgroundDirective)这些指令仅适用于子项,因此您可以选择更好的命名约定(例如 ApplyToControlBackgroundDirective)

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

相关问题 Angular:带有 ControlValueAccessor 的自定义输入 - Angular: custom input with ControlValueAccessor 如何使用 ControlValueAccessor 将 Angular 中的自定义输入标记为已触摸? - How to mark as touched a custom input in Angular with ControlValueAccessor? 如何在自定义 ControlValueAccessor 中使用内置 Angular 验证器 - How to use Built-in Angular validator within custom ControlValueAccessor 如何在Angular ControlValueAccessor指令中订阅FormControl值更改 - How Can I Subscribe to FormControl Value Changes In an Angular ControlValueAccessor Directive Angular:输入无线电不维护使用 ControlValueAccessor 实现的自定义组件中的选择 - Angular: Input radios not maintaining selection in custom component implemented with ControlValueAccessor Angular:ControlValueAccessor vs @Input - 什么时候用表单? - Angular: ControlValueAccessor vs @Input - When to use what with forms? Angular 2-ControlValueAccessor-如何实现? - Angular 2 - ControlValueAccessor - How to implement? Angular | ControlValueAccessor 到非本机输入 - Angular | ControlValueAccessor to non native Input 如何使用ControlValueAccessor? - How to use ControlValueAccessor? 如何在 Angular 2 RC6 中使用自定义指令? - How to use a custom directive in Angular 2 RC6?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM