I am trying to use FormBuilder in a page I have in Ionic 2.
First, here is my environment details: Running on Windows 10, and running ionic --version gives me 2.0.0-beta.35
Here is part of my package.json file:
...
"@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"
...
Second, here are the two main files involved:
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>
As you can see, I am trying to pass the FormGroup Object into multiple components so that I could use them.
Here is an example of what one of the components look like (minimal version right now):
<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>
The problem I am facing right now is this error:
What I believe is happening is that the FormBuilder is looking for the given names I declare in my typescript file (such as todo, imageUrl, link, etc), but since it is in my other components, it errors out, thinking its not there.
What could be the reason for this error? I have looked online and could not find related issues.
FYI, the reason I am needing them in components and not in the same page, is because in the future, the functionality will be different for each input, thus needed to give each component a "Single Responsibility".
Thanks in advance
For every one else with problem
Cannot find control with unspecified name attribute.
The problem is you forgot to define formControlName
on your form input elements.
formControlName="url"
If you are facing No provider for NgControl
after fixing inputs you have a problem with Ionic2 breaking changes in Fom handling. There is a chance you can fix your code by importing a new form component:
import { disableDeprecatedForms, provideForms } from '@angular/forms';
But you will probably still face more and more problems. To really fix your code:
Good tutorial about FormBuilder and validation https://blog.khophi.co/ionic-2-forms-formbuilder-and-validation/
As a general note in Angular 2+, I found that adopting a consistent approach to specifying formGroupName, formArrayName, and formGroupNamesaved a great deal of time, and what remained of my sanity. I lost much time before I did.
Either use
formXXXName="literal value"
Or
formXXXName="{{value expression}}"
Or
[formXXXName]="'literal value'" (single tick within double tick)
Or
[formXXXName]="value expression"
I was continually mixing them, to my great sorrow.
Example from a recent project:
Case 1: Correct use of bracket notation.
<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>
Case 2: Correct use of non-bracket notation.
<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>
Please note in the above the use of "{{" and "}}" to compel Angular to treat what would normally be a literal string value (because of the use of the no-brackets syntax) as an EL expression.
Case 3: Incorrect use of bracket notation
<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>
In this case, the value of formArrayName, "notes", is treated as an EL expression. Since my component has no such member, it evaluates to nothing. Therefore, the following error is sustained:
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)
Fortunately, the line number given at the very bottom of the stack points to the line with the problem.
Case 4: Incorrect use of non-bracketing notation
<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>
In this case, the value of formControlName, "i", will be interpreted as a literal string, but we really intended to refer to the index variable 'i' on the containing *ngFor directive. The result of this error is:
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)
Thanks and kudos to all Angular2+ developers and experts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With