Hari
Hari

Reputation: 1623

Unable to assign form control to template variable in Reactive Forms - angular?

I am new to angular let say, i have reactive form like follow

ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
    email: [ '', Validators.compose([Validators.required, Validators.email])],
    password: this.formBuilder.group({
    first: [ '', Validators.required ],
    second: [ '', Validators.required,  ] 
    })
  });
}

and my angular template looks like follow

<div class="container">
  <form [formGroup]="registerFormGroup" 
    (ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
    <label for="email">Email</label>
    <input type="email" formControlName="email" placeholder="Enter Email" 
   class="form-control">
</div>
<div *ngIf="!registerFormGroup.get('password').get('first').valid" 
     class="alert alert-danger">

</div>

<div class="form-group text-center">
  <button type="submit" class="btn btn-success btn-lg" 
  [disabled]="!registerFormGroup.valid">Submit</button>
</div>

For example, email field has two validations such as required and email type validate so depends upon the error I have to display error message so in my template am using like

<div *ngIf="!registerFormGroup.get('email').valid && (registerFormGroup.get('email').touched)" 
  class="alert alert-danger">

  </div>

Instead of adding same registerFormGroup.get('email') again and again i trying to create template expression like #emailForm="registerFormGroup.get('email')" in

<input type="email" formControlName="email" placeholder="Enter Email" class="form-control" #emailForm="registerFormGroup.get('email')">

so that i can use use like

<div *ngIf="!emailForm.valid" class="alert alert-danger">

  </div>

but i am getting error like

compiler.es5.js:1690 Uncaught Error: Template parse errors: There is no directive with "exportAs" set to "registerFormGroup.get('email')" ("l> ]#emailForm="registerFormGroup.get('email')">

what mistake i made??

Upvotes: 1

Views: 8137

Answers (6)

Tao
Tao

Reputation: 1

I was also looking for a more elegant solution than calling form.get multiple times. Here is what I come up with using ngif

 <div class="col-sm-6" *ngIf="form.get('sponsor') as sponsor">

            <input type="text" formControlName="sponsor" id="sponsor" class="form-control" name="sponsor"
              [class.is-invalid]="sponsor.errors && sponsor.touched">

        </div>

using the as feature on ngIf to create a template variable

Upvotes: 0

TomerBu
TomerBu

Reputation: 1503

Although I agree with the accepted answer (you should have a dedicated method in your form component that will encapsulate the validation process) sometimes you need a quick check in the template:

The trick is to expose the formGroup from your component and use it like so:
Template:

<input id="name" class="form-control"
     formControlName="name" required
     [class.is-invalid]="
     f.name.invalid &&
     (f.name.touched ||
     f.name.touched.dirty)">


Component:

  //easily access your form fields
  get f() { return this.checkoutForm.controls; }

Upvotes: 0

Tomas
Tomas

Reputation: 3436

Angular team strongly discourage from using functions outputs in templates due to change detection strategy. You might be interested in following solution, using ngForm directive:

<div class="container">
  <form [formGroup]="registerFormGroup" 
    (ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
    <label for="email">Email</label>
    <input type="email" formControlName="email" placeholder="Enter Email " 
   class="form-control" #email="ngForm">
</div>
<div *ngIf="email.invalid" 
     class="alert alert-danger">

</div>

It's still a hush to copy-paste it in template, but at least you might have direct reference to control via it's template variable. I personally go with controller getter function still, but I'm posting this answer for sake of answer completeness.

Upvotes: 0

Rohan Fating
Rohan Fating

Reputation: 2133

You can create common function to access form like below:

validateFormControl(controName: string) {
    let control = registerFormGroup.get(controName);
    return control.invalid && control.touched;
}

In Templete use this call wherever you need, you just need to pass your control name in function, you can modify this function as per your need as well and you do not need to use form.get all the time. This makes your template more cleaner and performance efficient.

<div *ngIf="validateFormControl('email')" 
  class="alert alert-danger">
  error message
</div>

<div *ngIf="validateFormControl('password')" 
      class="alert alert-danger">
   error message
</div>

Upvotes: 3

Rahul Singh
Rahul Singh

Reputation: 19622

Create a method on the component that returns if the form is valid or not and return it in the component

  checkError(){ // either you can pass the form from the template or use the component for decleration
    return registerFormGroup.get('email').valid;
  }

In the template call

  <div *ngIf="checkError()" class="alert alert-danger">
    // Always make sure not to use controls in the template it will result in AOT compilation error
  </div>

Upvotes: 1

Chandru
Chandru

Reputation: 11184

try this :

component.html

<div class="container">
    <form [formGroup]="registerFormGroup" 
    (ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
    <div class="form-group" [ngClass]="{'has-error':!registerFormGroup.controls['email'].valid}">
        <label for="email">Email</label>
        <input type="email" formControlName="email" placeholder="Enter Email" 
        class="form-control">
        <p class="alert alert-danger" *ngIf="registerFormGroup.controls['email'].dirty && !registerFormGroup.controls['email'].valid">Invalid email address</p>
    </div>
    <div class="form-group text-center">
        <button type="submit" class="btn btn-primary" [disabled]="!registerFormGroup.valid">Submit</button>
    </div>
</form>
</div>

component.ts

import { FormGroup, FormBuilder, Validators } from '@angular/forms';

export class AppComponent implements OnInit {
registerFormGroup: any;
constructor(
        private formBuilder: FormBuilder
    ) {}

ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
            email: [null , Validators.compose([Validators.required, Validators.email])]
        });
}
}

Upvotes: 0

Related Questions