web-dev-qa-db-fra.com

Comment utiliser Angular 2 FormBuilder entre plusieurs composants

J'essaie d'utiliser FormBuilder dans une page de Ionic 2. 

Premièrement, voici les détails de mon environnement: Sous Windows 10, et ionic --version _ me donne 2.0.0-beta.35

Voici une partie de mon fichier package.json:

...
"@angular/common": "2.0.0-rc.3",
"@angular/compiler": "2.0.0-rc.3",
"@angular/core": "2.0.0-rc.3",
"@angular/forms": "^0.3.0",
"@angular/http": "2.0.0-rc.3",
"@angular/platform-browser": "2.0.0-rc.3",
"@angular/platform-browser-dynamic": "2.0.0-rc.3",
"ionic-angular": "2.0.0-beta.10",
"ionic-native": "1.3.2",
"ionicons": "3.0.0"
...

Deuxièmement, voici les deux principaux fichiers impliqués:

insight.ts

import { Component } from '@angular/core';
import {NavController, NavParams} from 'ionic-angular';
import {
  REACTIVE_FORM_DIRECTIVES,
  FormBuilder,
  FormControl,
  FormGroup
} from '@angular/forms';
import { App, Insight } from '../../models';
import { InsightProvider } from '../../providers/insight/insight.service';
import { InsightImage, InsightLabel, InsightLink, InsightQuestion, InsightThought, InsightTodo, InsightVideo } from './shared';

@Component({
  templateUrl: 'build/pages/insight/insight.html',
  directives: [REACTIVE_FORM_DIRECTIVES, InsightImage, InsightLabel, InsightLink, InsightQuestion, InsightThought, InsightTodo, InsightVideo],
  providers: [App, InsightProvider, FormBuilder]
})
export class InsightPage {

  canAdd: boolean;
  showDetails: boolean;
  newInsight: Insight;
  insightForm: FormGroup;

  constructor(private insightProvider: InsightProvider,
              private params: NavParams) {
    this.insightForm = new FormGroup({
      type: new FormControl('', []),
      todo: new FormControl('', []),
      checked: new FormControl(false, []),
      imageUrl: new FormControl('', []),
      link: new FormControl('', []),
      url: new FormControl('', []),
      label: new FormControl('', []),
      question: new FormControl('', []),
      answer: new FormControl('', []),
      title: new FormControl('', []),
      details: new FormControl('', []),
    });
  }

  ngOnInit() {
    this.canAdd = false;
    this.showDetails = true;
  }

  addNewInsight() {
    if (this.newInsight.type) {
      this.insightProvider.createInsight(this.newInsight)
      .subscribe(response => {
        this.newInsight.setId(response.data.id);
        this.newInsight.title = '';
        console.log(response);
      });
    }
  }

  deleteClicked(index: number) {
    console.log('Clicked on ' + index);
    this.insightProvider.deleteInsight(this.newInsight)
    .subscribe(data => {
      console.log(data);
    });
  }


}

insight.html

<form [ngFormModel]="insightForm" (ngSubmit)="createNewInsight()">
      <ion-item>
        <ion-label for="type">Insight Type</ion-label>
        <ion-select name="type" id="type" [formControl]="type">
          <ion-option value="label">Label</ion-option>
          <ion-option value="thought">Thought</ion-option>
          <ion-option value="link">Link</ion-option>
          <ion-option value="question">Question</ion-option>
          <ion-option value="todo">Todo</ion-option>
          <ion-option value="image">Image</ion-option>
          <ion-option value="video">Video</ion-option>
        </ion-select>
      </ion-item>

      <div [ngSwitch]="type">
          <insight-image [form]="insightForm" *ngSwitchCase="'image'"></insight-image>
          <insight-label [form]="insightForm" *ngSwitchCase="'label'"></insight-label>
          <insight-link [form]="insightForm" *ngSwitchCase="'link'"></insight-link>
          <insight-question [form]="insightForm" *ngSwitchCase="'question'"></insight-question>
          <insight-thought [form]="insightForm" *ngSwitchCase="'thought'"></insight-thought>
          <insight-todo [form]="insightForm" *ngSwitchCase="'todo'"></insight-todo>
          <insight-video [form]="insightForm" *ngSwitchCase="'video'"></insight-video>
      </div>

      <button type="submit" block primary text-center (click)="addNewInsight()" [disabled]="!newInsight.type">
        <ion-icon name="add"></ion-icon> Add Insight
      </button>
    </form>

Comme vous pouvez le constater, j'essaie de transmettre l'objet FormGroup à plusieurs composants afin de pouvoir les utiliser.

Voici un exemple de l'un des composants (version minimale pour le moment):

<ion-item>
  <ion-label floating for="link">Link</ion-label>
  <ion-input type="text" name="link" id="link" [formControl]="link"></ion-input>
</ion-item>

<ion-item>
  <ion-label floating for="url">URL</ion-label>
  <ion-input type="text" id="url" name="url" [formControl]="url"></ion-input>
</ion-item>

Le problème auquel je suis confronté actuellement est cette erreur:

 Error from FormBuilder and Ionic 2

Ce que je crois, c'est que FormBuilder recherche les noms donnés que je déclare dans mon fichier TypeScript (tels que todo, imageUrl, lien, etc.), mais comme il se trouve dans mes autres composants, il est erroné, pensant qu'il n'est pas là. .

Quelle pourrait être la raison de cette erreur? J'ai regardé en ligne et n'ai pas trouvé de problèmes connexes.

Pour votre information, la raison pour laquelle j'en ai besoin dans des composants et non dans la même page, c'est parce qu'à l'avenir, les fonctionnalités seront différentes pour chaque entrée. Il est donc nécessaire de donner à chaque composant une "responsabilité unique".

Merci d'avance

7
Daniel Hair

Pour tout le monde avec problème 

Impossible de trouver le contrôle avec l'attribut de nom non spécifié.

Le problème est que vous avez oublié de définir formControlName sur vos éléments de saisie de formulaire.

formControlName="url"

Si vous êtes confronté à No provider for NgControl après la correction des entrées, vous rencontrez un problème lié à la rupture des modifications apportées par Ionic2 dans la gestion de Fom. Il est possible que vous corrigiez votre code en important un nouveau composant de formulaire:

import { disableDeprecatedForms, provideForms } from '@angular/forms';

Mais vous devrez probablement faire face à de plus en plus de problèmes. Pour vraiment réparer votre code:

  • mise à jour vers la dernière version bêta
  • simplifiez votre formulaire en 2 entrées simples
  • réécrivez votre formulaire et faites-le fonctionner
  • ajoutez le reste de vos éléments

Bon tutoriel sur FormBuilder et la validation https://blog.khophi.co/ionic-2-forms-formbuilder-and-validation/

20
dafyk

Comme note générale dans Angular 2+, j’ai constaté que l’adoption d’une approche cohérente pour spécifier formGroupName, formArrayName et formGroupNames permettait de gagner beaucoup de temps et de préserver ma santé mentale. J'ai perdu beaucoup de temps avant moi.

Soit utiliser 

formXXXName="literal value"

Ou 

formXXXName="{{value expression}}"

Ou

[formXXXName]="'literal value'" (single tick within double tick)

Ou 

[formXXXName]="value expression"

Je les mélangeais continuellement, à mon grand chagrin.

Exemple d'un projet récent:

Cas 1: Utilisation correcte de la notation entre crochets.

    <label>Notes</label>
    <div class="panel panel-default">
        <div class="panel-body">
            <div [formArrayName]="'notes'">
                <div *ngFor="let note of outletForm.controls.notes.controls; let i=index">
                    <input type="text" [formControlName]="i" class="form-control" />
                </div>
            </div>
        </div>
    </div>

Cas 2: Utilisation correcte de la notation sans crochet.

    <label>Notes</label>
    <div class="panel panel-default">
        <div class="panel-body">
            <div formArrayName="notes">
                <div *ngFor="let note of outletForm.controls.notes.controls; let i=index">
                    <input type="text" formControlName="{{i}}" class="form-control" />
                </div>
            </div>
        </div>
    </div>

Veuillez noter ci-dessus l'utilisation de "{{" et "}}" pour obliger Angular à traiter ce qui serait normalement une valeur de chaîne littérale (en raison de l'utilisation de la syntaxe sans crochets) en tant qu'expression EL .

Cas 3: utilisation incorrecte de la notation entre crochets

    <label>Notes</label>
    <div class="panel panel-default">
        <div class="panel-body">
            <div [formArrayName]="notes">
                <div *ngFor="let note of outletForm.controls.notes.controls; let i=index">
                    <input type="text" formControlName="{{i}}" class="form-control" />
                </div>
            </div>
        </div>
    </div>

Dans ce cas, la valeur de formArrayName, "notes", est traitée comme une expression EL. Comme mon composant n'a pas de membre de ce type, il n'évalue rien Par conséquent, l'erreur suivante est générée:

OutletFormComponent.html:153 ERROR Error: Cannot find control with unspecified name attribute
    at _throwError (forms.es5.js:1830)
    at setUpFormContainer (forms.es5.js:1803)
    at FormGroupDirective.addFormArray (forms.es5.js:4751)
    at FormArrayName.ngOnInit (forms.es5.js:5036)
    at checkAndUpdateDirectiveInline (core.es5.js:10793)
    at checkAndUpdateNodeInline (core.es5.js:12216)
    at checkAndUpdateNode (core.es5.js:12155)
    at debugCheckAndUpdateNode (core.es5.js:12858)
    at debugCheckDirectivesFn (core.es5.js:12799)
    at Object.eval [as updateDirectives] (OutletFormComponent.html:159)

Heureusement, le numéro de ligne indiqué tout en bas de la pile pointe vers la ligne présentant le problème.

Cas 4: Utilisation incorrecte de la notation sans crochet

    <label>Notes</label>
    <div class="panel panel-default">
        <div class="panel-body">
            <div [formArrayName]="'notes'">
                <div *ngFor="let note of outletForm.controls.notes.controls; let i=index">
                    <input type="text" formControlName="i" class="form-control" />
                </div>
            </div>
        </div>
    </div>

Dans ce cas, la valeur de formControlName, "i", sera interprétée comme une chaîne littérale, mais nous voulions vraiment faire référence à la variable d'index "i" de la directive conteneur * ngFor. Le résultat de cette erreur est:

OutletFormComponent.html:161 ERROR Error: Cannot find control with path: 'notes -> i'
    at _throwError (forms.es5.js:1830)
    at setUpControl (forms.es5.js:1738)
    at FormGroupDirective.addControl (forms.es5.js:4711)
    at FormControlName._setUpControl (forms.es5.js:5299)
    at FormControlName.ngOnChanges (forms.es5.js:5217)
    at checkAndUpdateDirectiveInline (core.es5.js:10790)
    at checkAndUpdateNodeInline (core.es5.js:12216)
    at checkAndUpdateNode (core.es5.js:12155)
    at debugCheckAndUpdateNode (core.es5.js:12858)
    at debugCheckDirectivesFn (core.es5.js:12799)

Merci et bravo à tous les développeurs et experts Angular2 +. 

3
jgenoese

Lorsque vous utilisez FormBuilder, vous devez définir le contrôle dans votre modèle afin que FormBuilder puisse l'identifier.

Donc, dans votre modèle qui a un formGroup nommé 'insightForm' avec un contrôle 'url':

<form [formGroup]="insightForm" (ngSubmit)="onSubmit(insightForm.value)">
    <ion-input [formControl]="insightForm.controls['url']"></ion-input>
</form>
0
Kavo