Passing formControlName into Angular component

2019-08-07 19:32发布

I have a reactive form. The setup is similar to this:

myForm: FormGroup;

    this.myForm= new FormGroup({
        name: new FormControl("", [Validators.required, Validators.maxLength(15), Validators.pattern('...')]),
        ...
    });

I use this on my form like this:

  <input
    type="text"
    formControlName="name"
  />
  <div *ngIf="name.errors?.required">
      Name is required
  </div>
  <div *ngIf="name.errors?.maxlength">
      Name must be {{ name.errors.maxlength.requiredLength }} characters        
  </div>
  <div *ngIf="name.errors?.pattern">
      Name has invalid characters.
  </div>

This is just a cut down version of my form. I have multiple input and I've had to create the error div's for each input.

So to fix this I've tried to create a component. The component is very similar to the code above:

  <input
    type="text"
    [formControlName]="formControlName"
  />
  <div *ngIf="name.errors?.required">
      Name is required
  </div>
  etc...

ts file:

@Component({
  selector: 'app-text',
  templateUrl: './text.component.html'
})
export class TextComponent  {

  @Input() formControlName: FormControl;
}

So on my form I'd like to use this component as follows:

<app-text [formControlName]="name"></app-text>

But I can't get this to work with the formControlName property.

Is this possible?

Thanks

I'm nearly there.

I've create this StackBlitz so show my progress:

Demo

Just struggling with the errors now and how to access the formControl to check for those errors

2条回答
We Are One
2楼-- · 2019-08-07 20:27

You need to pass the form control to the input element ,

  <input

    [value]="val"
    type="text"
    (input)="val=$event.target.value;onChange($event.target.value)"
    (blur)="onTouched()"
    [formControl]="control"

  >


  <span *ngIf="control && !control.valid && control.touched">
    <span class="error" *ngIf="control.errors['required']"> The field should not be empty</span>
    <span class="error" *ngIf="control.errors['email']"> The field should be an email 
    </span>
  </span>

Get the control in your custom component as input and show the error based on this.

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

 export class TextboxComponent extends DefaultValueAccessor implements OnInit {

 val:string = '';

 @Input('w-formcontrol') control;

 ngOnInit() {
 }

 writeValue(value: any): void {
  if(value) {
   this.val = value;
  }
 }
}
查看更多
▲ chillily
3楼-- · 2019-08-07 20:36

If you want access to the formControl the best thing to use is NgControl as a DI

Reasoning:

  • NgModel
  • FormControlDirective
  • FormControlName

Are all sub-classes of NgControl so you will be doing yourself a great deal of help if you did it this way so that if down the line you change your mind about using formControl to NgControl etc... you will have already covered those bases by using NgControl

So by example it would look something like

import { Component, OnInit, Self } from '@angular/core';

import { NgControl } from '@angular/forms';

@Component({
  selector: 'app-text',
  templateUrl: './text-box.component.html',
  styleUrls: ['./text-box.component.css']
})
export class TextBoxComponent implements OnInit {

  constructor(@Self() private ngControl: NgControl) { }

  ngOnInit() {
    console.log(this.ngControl);
  }

}

Html

<app-text[formControl]="control"></app-text>

The reason we use @Self is so that the component does not look further up the injector tree to find a ngControl but only on it's element. So you can nest etc...

This is very beneficial to directives as well but so far i hope this helped!

查看更多
登录 后发表回答