69 votes

Accéder au FormControl depuis le composant de formulaire personnalisé en Angular

J'ai un composant de contrôle de formulaire personnalisé dans mon application Angular, qui implémente ControlValueAccessor interface.

Cependant, je veux accéder à la FormControl instance, associée à mon composant. J'utilise des formulaires réactifs avec FormBuilder et fournir un contrôle de formulaire en utilisant formControlName attribut.

Alors, comment puis-je accéder FormControl à l'intérieur de mon composant de formulaire personnalisé ?

60voto

Slava Fomin II Points 1141

Cette solution est née de la discussion dans le référentiel Angular. N'hésitez pas à le lire ou, mieux encore, à y participer si vous êtes intéressé par ce problème.


J'ai étudié le code de FormControlName et cela m'a inspiré pour écrire la solution suivante :

@Component({
  selector: 'my-custom-form-component',
  templateUrl: './custom-form-component.html',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: CustomFormComponent,
    multi: true
  }]
})
export class CustomFormComponent implements ControlValueAccessor, OnInit {

  @Input() formControlName: string;

  private control: AbstractControl;

  constructor (
    @Optional() @Host() @SkipSelf()
    private controlContainer: ControlContainer
  ) {
  }

  ngOnInit () {

    if (this.controlContainer) {
      if (this.formControlName) {
        this.control = this.controlContainer.control.get(this.formControlName);
      } else {
        console.warn('Missing FormControlName directive from host element of the component');
      }
    } else {
      console.warn('Can\'t find parent FormGroup directive');
    }

  }

}

J'injecte le parent FormGroup au composant et ensuite obtenir le FormControl à partir de celui-ci en utilisant le nom du contrôle obtenu par formControlName la reliure.

Toutefois, il faut savoir que cette solution est conçue spécifiquement pour le cas d'utilisation où FormControlName est utilisée sur l'élément hôte. Elle ne fonctionnera pas dans les autres cas. Pour cela, vous devrez ajouter une logique supplémentaire. Si vous pensez que ce problème devrait être résolu par Angular, consultez le site suivant la discussion .

0 votes

D'où avez-vous this.control ?

0 votes

@DAG comme je l'ai indiqué dans la réponse : I'm injecting the parent FormGroup to the component and then getting the specific FormControl from it using control name obtained through formControlName binding.

7 votes

@SlavaFominII : Au lieu d'injecter le groupe de formulaires parent et d'accéder ensuite au contrôle en utilisant la liaison d'entrée formControllerName, ne pouvons-nous pas simplement passer le contrôle du formulaire comme liaison d'entrée ? J'avais une exigence similaire où je voulais accéder au contrôle de formulaire à partir d'un composant de formulaire personnalisé et j'ai passé ce contrôle de formulaire comme une liaison d'entrée au composant de formulaire personnalisé.

53voto

Randy Points 1164

Utilisation de formControlName en tant que paramètre d'entrée ne fonctionne pas lorsque la liaison se fait par le biais de l'option [formControl] directive.

Voici une solution qui fonctionne dans les deux sens sans aucun paramètre d'entrée.

export class MyComponent implements AfterViewInit {

  private control: FormControl;

  constructor(
    private injector: Injector,
  ) { }

  // The form control is only set after initialization
  ngAfterViewInit(): void {
    const ngControl: NgControl = this.injector.get(NgControl, null);
    if (ngControl) {
      this.control = ngControl.control as FormControl;
    } else {
      // Component is missing form control binding
    }
  }
}

5 votes

Cette solution est définitivement meilleure que la réponse acceptée.

0 votes

Bien que cette solution ait fonctionné à merveille pour moi aussi, et semble bien plus élégante que celle acceptée, dans Angular 7, j'obtiens cette erreur tslint : get is deprecated: from v4.0.0 use Type<T> or InjectionToken<T>

0 votes

Il devrait être assez facile d'adapter cette solution pour utiliser InjectionToken<NgControl>.

13voto

Rui Marques Points 3152

En s'appuyant sur les réponses précédentes et documentation trouvé dans un commentaire, voici ce qui, à mon avis, est la solution la plus propre pour une ControlValueAccessor basé sur le composant.

// No FormControl is passed as input to MyComponent
<my-component formControlName="myField"></my-component>

export class MyComponent implements AfterViewInit, ControlValueAccessor  {

  constructor(@Optional() @Self() public ngControl: NgControl) {
    if (ngControl != null) {
      // Setting the value accessor directly (instead of using
      // the providers) to avoid running into a circular import.
      ngControl.valueAccessor = this;
    }
  }

    ngAfterContentInit(): void {
       const control = this.ngControl && this.ngControl.control;
       if (control) {
          // FormControl should be available here
       }
    }
}

4voto

Yuri Beliakov Points 88

Comme @Ritesh l'a déjà écrit dans le commentaire, vous pouvez passer le contrôle de formulaire comme une liaison d'entrée :

<my-custom-form-component [control]="myForm.get('myField')" formControlName="myField">
</my-custom-form-component>

Et ensuite vous pouvez obtenir l'instance du contrôle de formulaire dans votre composant de formulaire personnalisé comme ceci :

@Input() control: FormControl;

3voto

Brad C Points 2434

Voici une version simplifiée/épurée de la réponse acceptée qui fonctionne pour les entrées FormControlName et FormControl :

export class CustomFormComponent implements ControlValueAccessor, OnInit {

  @Input() formControl: FormControl;

  @Input() formControlName: string;

  // get ahold of FormControl instance no matter formControl or formControlName is given.
  // If formControlName is given, then controlContainer.control is the parent FormGroup/FormArray instance.
  get control() {
    return this.formControl || this.controlContainer.control.get(this.formControlName);
  }

  constructor(private controlContainer: ControlContainer) { }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X