82

I have simple custom input component like this,

import {Component, Provider, forwardRef} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";

const noop = () => {};

const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => CustomInputComponent),
  multi: true
};

@Component({
  selector: 'custom-input',
  template: `

          <input class="form-control" 
                 [(ngModel)]="value" name="somename"
                 (blur)="onTouched()">

  `,
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CustomInputComponent implements ControlValueAccessor{

  //The internal data model
  private _value: any = '';

  //Placeholders for the callbacks
  private _onTouchedCallback: () => void = noop;

  private _onChangeCallback: (_:any) => void = noop;

  //get accessor
  get value(): any { return this._value; };

  //set accessor including call the onchange callback
  set value(v: any) {
    if (v !== this._value) {
      this._value = v;
      this._onChangeCallback(v);
    }
  }

  //Set touched on blur
  onTouched(){
    this._onTouchedCallback();
  }

  //From ControlValueAccessor interface
  writeValue(value: any) {
    this._value = value;
  }

  //From ControlValueAccessor interface
  registerOnChange(fn: any) {
    this._onChangeCallback = fn;
  }

  //From ControlValueAccessor interface
  registerOnTouched(fn: any) {
    this._onTouchedCallback = fn;
  }

}

and I have app module like this,

/**
 * Created by amare on 8/15/16.
 */
import { NgModule }                     from '@angular/core';
import { BrowserModule }                from '@angular/platform-browser';
import { ReactiveFormsModule, FormsModule }          from '@angular/forms';
import { AppComponent }                 from './app/app.component';
import {CustomInputComponent} from "./app/shared/custom.input.component";
import {RouterModule} from "@angular/router";
@NgModule({
  imports: [ BrowserModule, ReactiveFormsModule, FormsModule, RouterModule ],
  declarations: [ AppComponent, CustomInputComponent],
  bootstrap: [ AppComponent ]
})
export class AppModule {  
}

and main

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule }              from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

and I have used my custom input in one of my components as shown below, but am getting 'No value accessor for form control with unspecified name attribute'.

<custom-input name="firstName" [(ngModel)]="firstName"></custom-input>

and the app.component looks like this

import { Component } from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  title = 'app works!';

  firstName: string;
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Amare
  • 1,669
  • 1
  • 12
  • 11
  • I have the same issue with a custom directive that implements ControlValueAccessor. It was working in RC4 but gets same error as you for RC5. Hoping someone has a solution. – user2444499 Aug 16 '16 at 23:50
  • 39
    Try adding `ngDefaultControl` to your control, like this: `` – danieleds Aug 17 '16 at 15:08
  • @danieleds thanks so much it works like a charm, even though the angular team was not responding. – Amare Aug 18 '16 at 06:35
  • 1
    Beware of any 3rd-party packages you may be using - if you import anything that is making use of the old `FORM_DIRECTIVES`, it will break your app! Case in point: https://github.com/valor-software/ng2-charts/issues/352 – Pete Aug 19 '16 at 11:18
  • For me adding ngDefaultControl before [(ngModel)]="...." worked – themightysapien Feb 17 '17 at 15:11

5 Answers5

74

adding ngDefaultControl to the custom input component in the host solved the issue, thanks @danieleds

Amare
  • 1,669
  • 1
  • 12
  • 11
  • 9
    This solution did not work for me. I have a name applied to my `input` and i also tried adding `ngDefaultControl` to the input and it did not work. Still gave me the same error. In RC5 – prolink007 Aug 24 '16 at 02:25
  • 2
    Agree, I added name property in html and ngDefaultControl and I use formControl and it does not work. – Stephan Kristyn Oct 17 '16 at 12:11
  • 1
    See a very good explanation on how `ngDefaultControl` works - https://stackoverflow.com/a/46465959/968003. Essentially, it adds default `ControlValueAccessor`, which acts as a bridge between the Angular forms API and a native element in the DOM. – Alex Klaus Oct 19 '17 at 05:07
46

Add ngDefaultControl to the custom-input component. This adds two-way databinding, you shouldn't have to implement the value accessor methods unless you are doing something unique.

<custom-input name="firstName" [(ngModel)]="firstName" ngDefaultControl></custom-input>
Nick
  • 978
  • 1
  • 12
  • 28
13

Add ngDefaultControl to your input. eg

<inline-editor type="textarea" [(ngModel)]="editableTextArea" (onSave)="saveEditable($event)" value="valor" ngDefaultControl> </inline-editor> 

Then import { FORM_DIRECTIVES } from '@angular/common';

Finally directives: [FORM_DIRECTIVES]

This will work :) Thanks for the above comments

styopdev
  • 2,604
  • 4
  • 34
  • 55
4

I was putting [(ngModel)] on my <option> tag instead of <select>

So yeah... that will cause this.

Ryan Knell
  • 6,204
  • 2
  • 40
  • 32
0

As yet another scenario where this comes up, I had [(ngModel)] specified on a custom component which was recently rebuilt and simplified, and the new version of the component just had ngModel as a normal input variable with emits.

This was not enough - the input variable must be renamed to something besides ngModel, or the component must implement the ControlValueAccessor interface (see the docs for details). Once one or the other is complete, you will no longer get this error.

bsplosion
  • 2,641
  • 27
  • 38