web-dev-qa-db-ja.com

複数のコンポーネント間でAngular 2のFormBuilderを使用する方法

Ionic 2にあるページでFormBuilderを使用しようとしています。

まず、私の環境の詳細は次のとおりです。Windows10で実行し、ionic --versionを実行すると、2.0.0-beta.35

これが私の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"
...

次に、関連する2つの主要なファイルは次のとおりです。

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>

ご覧のとおり、FormGroupオブジェクトを複数のコンポーネントに渡して、それらを使用できるようにしようとしています。

コンポーネントの1つがどのように見えるかの例を次に示します(現在の最小バージョン):

<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>

私が今直面している問題はこのエラーです:

Error from FormBuilder and Ionic 2

FormBuilderは、TypeScriptファイルで宣言した名前(todo、imageUrl、linkなど)を探していますが、他のコンポーネントにあるため、エラーが発生し、そこにないと考えています。 。

このエラーの理由は何でしょうか?オンラインで調べましたが、関連する問題が見つかりませんでした。

参考までに、同じページではなくコンポーネントでそれらを必要とする理由は、将来、機能が入力ごとに異なるため、各コンポーネントに「単一責任」を与える必要があるためです。

前もって感謝します

7
Daniel Hair

問題のある他のすべての人のために

名前属性が指定されていないコントロールが見つかりません。

問題は、フォーム入力要素でformControlNameを定義するのを忘れたことです。

formControlName="url"

あなたが直面している場合No provider for NgControl入力を修正した後、Fom処理の変更を壊すIonic2に問題があります。新しいフォームコンポーネントをインポートすることで、コードを修正できる可能性があります。

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

しかし、あなたはおそらくまだますます多くの問題に直面するでしょう。コードを実際に修正するには:

  • 最新のベータ版に更新する
  • フォームを2つの単純な入力に単純化します
  • フォームを書き直して機能させる
  • 残りの要素を追加します

FormBuilderと検証に関する優れたチュートリアル https://blog.khophi.co/ionic-2-forms-formbuilder-and-validation/

20
dafyk

Angular 2+)の一般的な注意として、formGroupName、formArrayName、formGroupNameを指定するための一貫したアプローチを採用することで、多くの時間が節約され、正気が失われたことがわかりました。多くの時間を失いました。私がする前に。

どちらかを使用

formXXXName="literal value"

または

formXXXName="{{value expression}}"

または

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

または

[formXXXName]="value expression"

私は絶えずそれらを混ぜ合わせていました、私の大きな悲しみに。

最近のプロジェクトの例:

ケース1:角かっこ表記の正しい使用。

    <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>

ケース2:括弧以外の表記の正しい使用。

    <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>

上記では、「{{」と「}}」を使用してAngularを強制し、通常はリテラル文字列値になるものを処理することに注意してください(角かっこなしの構文を使用しているため) )EL式として。

ケース3:角かっこ表記の誤った使用

    <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>

この場合、formArrayNameの値「notes」はEL式として扱われます。私のコンポーネントにはそのようなメンバーがないため、評価はありません。したがって、次のエラーが発生します。

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)

幸い、スタックの一番下に表示されている行番号は、問題のある行を指しています。

ケース4:ブラケット以外の表記の誤った使用

    <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>

この場合、formControlNameの値 "i"はリテラル文字列として解釈されますが、実際には、含まれている* ngForディレクティブのインデックス変数 'i'を参照することを意図しています。このエラーの結果は次のとおりです。

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)

すべてのAngular2 +開発者と専門家に感謝と称賛を送ります。

3
jgenoese

FormBuilderを使用する場合、FormBuilderがコントロールを識別できるように、テンプレートでコントロールを定義する必要があります。

したがって、コントロール「url」を持つ「insightForm」という名前のformGroupを持つテンプレートでは、次のようになります。

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