3

I was trying to implement what seemed to be a straightforward form validation routine following this walk-through on my ionic 2 projects.

In my controller's constructor I used the FormBuilder like this to make a formGroup:

this.form = formBuilder.group({
  date: ['', Validators.required],
  client: ['', Validators.required]
});

And then in the template I added formControllerName attribute to relevant elements like this:

<ion-select formControlName="client" [(ngModel)]="clientId">

And bound the root element to the 'formGroup` like this:

<ion-content [formGroup]="form">

This fails with the following error message:

ngModel cannot be used to register form controls with a parent formGroup directive. Try using formGroup's partner directive "formControlName" instead. Example:

<div [formGroup]="myGroup">
  <input formControlName="firstName">
</div>

In your class:

this.myGroup = new FormGroup({
   firstName: new FormControl()
});

  Or, if you'd like to avoid registering this form control, indicate that it's standalone in ngModelOptions:

  Example:


<div [formGroup]="myGroup">
   <input formControlName="firstName">
   <input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
</div>

As suggested by the error message, as well as this StackOverflow thread I added [ngModelOptions]="{standalone: true}" to my inputs but it came back with another error message:

Template parse errors: Can't bind to 'ngModelOptions' since it isn't a known property of 'ion-select'.

Roham Rafii
  • 2,929
  • 7
  • 35
  • 49
Vahid
  • 1,829
  • 1
  • 20
  • 35

1 Answers1

6

In angular2 you can choose to either use the "old" (more AngularJS-y) template-driven way, by using [(ngModel)] to create two-way bindings with your component variables:

Component:

private clientId: string;

Template:

<form>
    <input [(ngModel)]="clientId" required />
</form>

If I got facts together, this will behind-the-scenes create a FormGroup instance for the <form> element, and a FormControl instance for each input in it. The required directive will then apply a validator on it and trigger validation (and for example apply ng-valid/invalid accordingly).

Using this approach you can grab the value simply using clientId.

Using the model-driven (or "reactive") approach is by defining the form's "schema" using FormBuilder or simply creating instances of FormGroup and FormControl...

It would look something like this:

Component:

private form: FormGroup;

ngOnInit() {
    this.form = new FormGroup({
        clientId: new FormControl('', [Validators.required])
    });
}

Template:

<form [formGroup]="form">
    <input formControlName="clientId" />
</form>

In this case, if you want the entire form's value, you'd access (this.)form.value which in this example will return an object like this:

 { clientId: "whatever" }

Or if you only want the inner control's value, grab (this.)form.controls['clientId'].value.

Hope I made things clearer and not worse :)

EDIT: Using the latter approach means you can access an Observable of the FormControl and do some interesting things, for example:

let debouncedInput$ = this.form.control['clientId'].valueChanges.debounceTime(1000);
Amit
  • 4,274
  • 21
  • 25
  • 1
    Added one more edit, in my opinion this is what makes reactive forms so much better. – Amit Mar 08 '17 at 08:23
  • I did what you said but I'm getting this error now: `No value accessor for form control with unspecified name attribute`. Any chance you would also know what is that about? – Vahid Mar 08 '17 at 08:59
  • Can you post your template and component? – Amit Mar 08 '17 at 09:00
  • this.form = formBuilder.group({ name: ['', Validators.required] }); – Vahid Mar 08 '17 at 09:03
  • Hmm, seems `` doesn't have a ControlValueAccessor, which seems unlikely, maybe you're on an older version of angular or ionic? – Amit Mar 08 '17 at 09:05
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/137530/discussion-between-vahid-and-amit). – Vahid Mar 08 '17 at 09:07