【问题标题】:Angular: access FormControl from DirectiveAngular:从指令访问 FormControl
【发布时间】: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);
}


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

【问题讨论】:

    标签: angular typescript angular-directive


    【解决方案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 模板中的元素一样。
      猜你喜欢
      • 2021-03-10
      • 1970-01-01
      • 1970-01-01
      • 2016-05-20
      • 2018-09-26
      • 1970-01-01
      • 2016-12-24
      • 2017-06-24
      • 1970-01-01
      相关资源
      最近更新 更多