Paul Carlson
Paul Carlson

Reputation: 457

Angular unexpected error: Cant bind FormControl to input

I am looking for some help on trying to clear up, this all to common error. I am using forms in two different parts of my application, and the form works perfectly in one part, but not the other. I have imported both ReactiveFormsModule and FormsModule.

resolution.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { ResolutionRoutingModule } from './resolution-routing.module';
import { ResolutionComponent } from './resolution/resolution.component';
import { ResolutionDetailComponent } from './resolution-detail/resolution-detail.component';
import { ResolutionListComponent } from './resolution-list/resolution-list.component';
import { NewResolutionComponent } from '../../forms/new-resolution/new-resolution.component';
import { MatFormFieldModule } from '@angular/material/form-field';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatDialogModule } from '@angular/material/dialog';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatCardModule } from '@angular/material/card';
import { ResolutionEditComponent } from '../../forms/resolution-edit/resolution-edit.component';


@NgModule({
  declarations: [ResolutionComponent,
ResolutionDetailComponent,
ResolutionListComponent,
NewResolutionComponent,
ResolutionEditComponent],
  imports: [
    CommonModule,
    ResolutionRoutingModule,
    MatFormFieldModule,
    ReactiveFormsModule,
    MatDialogModule,
    FormsModule,
    MatInputModule,
    MatSelectModule,
    MatCardModule
  ]
})
export class ResolutionModule { }

In the component NewResolutionComponent my form works as expected, and I get no errors.

new-resolution-component.html(snippet: I only included one input function)

<form (ngSubmit)='onSubmit()'[formGroup]='ResolutionFormGroup'>
<mat-form-field>
        <mat-label>Title</mat-label>
        <input [formControl]='TitleControl' matInput placeholder="Title" required/>
</mat-form-field>
....
</form>

new-resolution.component.ts

import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
import { MatDialogRef } from '@angular/material/dialog';
import { AuthService } from '../../shared/services/auth.service';
import { ResolutionsService } from '../../shared/services/resolutions.service';

@Component({
  selector: 'app-new-resolution',
  templateUrl: './new-resolution.component.html',
  styleUrls: ['./new-resolution.component.scss']
})
export class NewResolutionComponent implements OnInit {
  uid: string
  category_id: string
  title: string
  description: string
  catId: string
  categoryList = []

  TitleControl: AbstractControl
  DescriptionControl: AbstractControl
  UserIdControl: AbstractControl
  CategoryControl: AbstractControl
  ResolutionFormGroup: FormGroup

  constructor(public as: AuthService,
    public rs: ResolutionsService,
    public dialog: MatDialogRef<NewResolutionComponent>) { }

    userid: string
    
  ngOnInit(): void {
    this.ResInit()
  }
  private ResInit() {
    this.ResolutionFormGroup = new FormGroup({})
    this.ResolutionFormGroup.registerControl('id',(this.UserIdControl = new FormControl('this',[Validators.required])))
    this.ResolutionFormGroup.registerControl('category_id',(this.CategoryControl = new FormControl('',[Validators.required])))
    this.ResolutionFormGroup.registerControl('title',(this.TitleControl = new FormControl('SUPER',[Validators.required])))
    this.ResolutionFormGroup.registerControl('description',(this.DescriptionControl = new FormControl('',[Validators.required])))
  }

This produces the desired output where my input has a placeholder of 'SUPER'. However, when I try to recreate this in another file, I get an entirely different result, and errors.

resolution-edit.component.html(snippet: only showing one input as the others are identical)

<form formGroup='ResEditGroup'>
    <mat-form-field>
        <mat-label>Title</mat-label>
        <input [FormControl]='TitleControl' matInput placeholder="Title" required/>
    </mat-form-field>

The first thing I notice is that around my FormControl I have '[]'. If I remove the brackets, my code will run without giving the error: Can't bind to 'FormControl' since it isn't a known property of 'input'.

resolution-edit.component.ts

import { Component, Inject, OnInit } from '@angular/core';
import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';

@Component({
  selector: 'app-resolution-edit',
  templateUrl: './resolution-edit.component.html',
  styleUrls: ['./resolution-edit.component.scss']
})
export class ResolutionEditComponent implements OnInit {
  ResEditGroup: FormGroup
  TitleControl: AbstractControl
  DescriptionControl: AbstractControl

  description: string
  title: string

  descriptionInput: string
  titleInput: string
  constructor(
    // @Inject(MAT_DIALOG_DATA) data
    ) {
    // this.descriptionInput = data.description
    // this.titleInput = data.title
   }

  ngOnInit(): void {
    this.EditInit()
  }
  EditInit(): void{
    this.ResEditGroup = new FormGroup({})
    this.ResEditGroup.registerControl('title',(this.TitleControl = new FormControl('SUPERBAD',[Validators.required])))  
    this.ResEditGroup.registerControl('description',(this.DescriptionControl = new FormControl('',[Validators.required])))
  }

}

Based on the comparison of both of my components, I would expect the input of the second component to display a placeholder of 'SUPERBAD', just as I had a placeholder of 'SUPER' in the first. However, in this case, I get no placeholder, except for what I declare directly inside the HTML input.
How do I get my second component to display 'SUPERBAD'?

Upvotes: 0

Views: 889

Answers (1)

Owen Kelvin
Owen Kelvin

Reputation: 15083

Before we try to answer what is wrong with the code, lets try to refactor it to a shorter code.

import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';

**resolution-edit.component.ts**

@Component({
  selector: 'app-resolution-edit',
  templateUrl: './resolution-edit.component.html',
  styleUrls: ['./resolution-edit.component.scss']
})
export class ResolutionEditComponent implements OnInit {
  resEditGroup = this.fb.group({
     title: ['SUPERBAD', [Validators.required]],
     description: ['', [Validators.required]]
  })
  constructor(
    private fb: FormBuilder,
    // @Inject(MAT_DIALOG_DATA) data
    ) {
    // this.descriptionInput = data.description
    // this.titleInput = data.title
   }

  ngOnInit(): void {
  }

}

With the above you can have resolution-edit.component.html

<form [formGroup]='ResEditGroup'>
    <mat-form-field>
        <mat-label>Title</mat-label>
        <input formControlName='title' matInput placeholder="Title" required/>
    </mat-form-field>

Below are the changes I have made.

  1. I am using a FormBuilder to generate my FormGroup. With this I dont need to create a new FormFroup and FormControl using new
  2. Changed to camelCase
  3. important I am binding to the FormGroup using [] and for the controls I am passing formControlName as a string. This is actually where you have a problem for you are passing in a FormGroup but not binding to ResEditGroup

Upvotes: 1

Related Questions