Angular:从指令访问 FormControl

Posted

技术标签:

【中文标题】Angular:从指令访问 FormControl【英文标题】:Angular: access FormControl from Directive 【发布时间】:2019-02-21 07:09:26 【问题描述】:

我想通过自定义指令将验证器动态添加到我的 FormControl。

@Directive(
    selector: "[idNumber]",
)
export class IdNumberDirective implements OnInit 

    constructor(private formControl: FormControl)  

    ngOnInit() 
        this.addValidators(this.formControl);
    

    addValidators(formControl: FormControl) 
        formControl.setValidators(Validators.compose(
            [Validators.required,
            Validators.minLength(3),
            Validators.maxLength(8)
            ]
        ));
    



<mat-form-field>
    <mat-label>label</mat-label>
    <input matInput
        [formControl]="idNumberFormControl"
        [placeholder]="placeholder"
</mat-form-field>

我不需要引用 nativeElement(通过 ElementRef)。 我想引用 formControl... ...并像这样使用它:

// html with my custom directive 'idNumber' ////////
<custom-input-string
    idNumber 
    [name]="'idNumber'"
    [label]="Id Number"
    [placeholder]="">
</custom-input-string>

// TS ////////
@ViewChild(CustomInputStringComponent) child: CustomInputStringComponent;

ngAfterViewInit() 
    setTimeout(() => 
        this.child.insertIntoForm(this.signupForm);
    , 0);

有什么想法吗? 谢谢大家。

【问题讨论】:

【参考方案1】:

您可以通过 FormGroupDirective 在指令中访问 FormGroup 和 FormControl:

注意:在本例中,我正在选择国家/地区。

import  FormGroupDirective  from "@angular/forms";

然后:


constructor(private fg: FormGroupDirective)  


// Access the FormGroup
console.log('My FormGroup values: ', this.fg.value);

// Access the FormControl
console.log('The selectedCountryCtrl: ', this.fg.control.controls.selectedCountryCtrl);
console.log('The selectedCountryCtrl value: ', this.fg.control.controls.selectedCountryCtrl.value);

// Access the variable/object directly
console.log('My FormControl selectedCountry value: ', this.fg.value.selectedCountry);

【讨论】:

【参考方案2】:

这是使用指令将验证器附加到表单控件的示例。

Stackblitz

请注意,使用它会导致丢失所有之前的验证器。

constructor(
  // Get the control directive
  private control: NgControl
)  
ngOnInit() 
  const abstractControl = this.control.control;
  abstractControl && abstractControl.setValidators([Validators.required]);

【讨论】:

嗨,onEvent 不是每次都在 FormControl 的 valueChanges 上触发吗?我的意思是,当用户在 FormControl 中聚焦时按下任何键时,这个事件不会触发吗?没有办法在指令的 OnInit 方法中检查和设置这些验证器吗?谢谢。 是的,它确实会在每次击键时触发,但函数中的条件会阻止验证器被设置多次。这必须以这种方式完成,因为构造函数不知道该控件。您也可以使用 ngOnInit(),如在 this stackblitz 中,我已经更新了我的答案(因为它确实更清楚!) 太棒了!正是我想要的!谢谢! 这个答案效果很好,但我认为主要方面(使用NgControl 而不是 OP 的FormControl)太有价值,不能留在外部链接(Stackblitz)。我建议我们将这部分直接放在答案中,就像@Suresh 在他的回答中所做的那样。【参考方案3】:
//TestAnythingsComponent.ts

import  Component, OnInit  from '@angular/core';
import  FormControl, FormBuilder, FormGroup, Validators  from '@angular/forms';
import  IdNumberDirective  from '../directives/IdNumberDirective.directive';

@Component(
  selector: 'app-test-anythings',
  templateUrl: './test-anythings.component.html',
  styleUrls: ['./test-anythings.component.css'],
  providers:[IdNumberDirective]
)
export class TestAnythingsComponent implements OnInit 
  testForm: FormGroup;

  constructor(fb: FormBuilder, IdNumberDirective : IdNumberDirective)  
    this.testForm = fb.group(
      idNumberFormControl : new FormControl(null,
          Validators.compose([
            Validators.required,
            Validators.minLength(3),
            Validators.maxLength(8),
          IdNumberDirective.customValidator()
          ])
        ),
    )
  


//IdNumberDirective.ts

import  Directive, OnInit  from '@angular/core';
import  Validators, ValidatorFn, AbstractControl, ValidationErrors  from '@angular/forms';

@Directive(
  selector: '[idNumber]'
)
export class IdNumberDirective implements OnInit 

  constructor() 

  

  ngOnInit() 

  

  customValidator(): ValidatorFn 
    Validators.nullValidator
    return (control: AbstractControl): ValidationErrors | null => 

      //any condition to check control value
      if (control.value != "Sachin") 
        //return key value pair of errors
        return  "customError":  inValid: true, errMsg: 'Invalid Value'  ;
      
      return null;
    
  



//test-anythings.component.html



    <form [formGroup]="testForm">
      <input idNumber formControlName="idNumberFormControl" />
      <div *ngIf="testForm.get('idNumberFormControl').invalid && testForm.get('idNumberFormControl').errors.customError.inValid"
        style="color:red">
        testForm.get('idNumberFormControl').errors.customError.errMsg
      </div>
    
      <button type="submit">submit</button>
    </form>

【讨论】:

亲爱的 Sachin,您能否在您的代码中添加一些注释以帮助理解您的方法? 嗨 Artem,看看我上面的解决方案,请输入“Sachin”以消失错误消息,我已经尽力了,我是 Angular 新手。谢谢。【参考方案4】:

如果您使用 NgControl 和构造函数 DI 注入,我们可以有一个指令适用于 formControlName 或模板驱动表单中的反应式表单的表单控件:

指令:

import  Directive  from "@angular/core";
import  NgControl  from "@angular/forms";

@Directive(
  selector: '[my-directive]'
)
export class MyDirective 
  constructor(private el: ElementRef, private control : NgControl)  


【讨论】:

嗨@Suresh,谢谢你的建议,但我需要更详细的解释。我能做到吗? this.addValidators(this.control);那行得通吗? PS:我不使用,也永远不会使用“模板驱动的表单”。 我们可以使用 NgControl 通过 DI 获得对表单控件实例的引用。您可以使用该实例访问表单控件方法 您能否用更多代码更新您的答案?另外 - 您的建议中的“私人 el:ElementRef”有什么意义? @PaxForce 在这种特殊情况下,private el: ElementRef 没有任何作用,您可以忽略它。但一般来说,ElementRef 会传递给每个指令,因此您可以访问底层元素,而像 this.el.nativeElement 这样的原生元素会从 DOM 返回 HTML 元素。就像在组件中使用 ViewChild 来引用 HTML 模板中的元素一样。

以上是关于Angular:从指令访问 FormControl的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 2.0 中使用 formControl 访问 Native HTML Input 元素

从“@angular/forms”导入 FormControl,FormGroup; [复制]

在新的 Angular 2 表单中将 NgModel 绑定到 FormControl

无法从'@angular/forms'导入“导入FormGroup,FormControl [重复]

如何从 Angular2 FormControl 对象中获取输入字段的名称?

表单中的 Angular 5 输入不会更新 formControl