Skip to content

Instantly share code, notes, and snippets.

@rlunaro
Last active July 30, 2024 17:11
Show Gist options
  • Save rlunaro/5bc52a1e97e587fdb726b0dc6a09d056 to your computer and use it in GitHub Desktop.
Save rlunaro/5bc52a1e97e587fdb726b0dc6a09d056 to your computer and use it in GitHub Desktop.
Angular Forms: template driven approach

Angular forms: template driven approach

In the template driven approach you define the form behaviour and configuration in the template: validators, and so on.

You can find a more detailed tutorial at: https://angular.dev/guide/forms/template-driven-forms#

We will start with this simple example made in ionic (but you can do in html exactly the same):

<ion-row>
  <ion-col>
   <ion-input name="jugadorName"
   placeholder="Nombre"
   autofocus="true"
   inputmode="text"
   autocorrect="off"
   type="text"></ion-input>
  </ion-col> 
 </ion-row>

Step 1: add a <form> tag

<!-- begin rest of the fields -->
<form> <-- HERE
<ion-row>
  <ion-col>
   <ion-input name="jugadorName"
   placeholder="Nombre"
   autofocus="true"
   inputmode="text"
   autocorrect="off"
   type="text"></ion-input>
  </ion-col> 
 </ion-row>

Step 2: tell angular what controls form part of your form

Add "ngModel" to every control to make angular aware that your control forms part of your form. You don't need to use the two-way binding, it's not needed:

<form>
<ion-row>
  <ion-col>
   <ion-input name="jugadorName"
   placeholder="Nombre"
   autofocus="true"
   inputmode="text"
   autocorrect="off"
   type="text" 
   ngModel></ion-input>
  </ion-col> 
 </ion-row>

If it is necesary, you can use the two-way binding to link the value in the form with a property in your component's code:

  <ion-input name="jugadorName"
  placeholder="Nombre"
  autofocus="true"
  inputmode="text"
  autocorrect="off"
  type="text" 
  [(ngModel)]="jugadorName"></ion-input>
export class EditarComponent {

  jugadorName: string; 

Step 3: import "FormsModule" in the module

 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
 
 
 @NgModule({
  imports: [
    CommonModule, 
    IonicModule,
    FormsModule,
    AdminJugadoresRoutingModule],
  declarations: [
    EquipoPipe, 
    AdminJugadoresPage, 
    BuscarComponent, 
    EditarComponent
  ]
})
export class AdminJugadoresModule{ }

Step 4: check that all your form elements have the name attribute

Like this:

 <ion-input name="jugadorName"
 placeholder="Nombre"
 autofocus="true"
 inputmode="text"
 autocorrect="off"
 type="text" 
 ngModel></ion-input>

Step 5: add a template variable to your form element

<form #datosJugador="ngForm">

With this, we will get access to the form data in the component code. We have to assign it the special value "ngForm" to let angular know that it is a form of type NgForm, that it will be created and managed by Angular.

Step 6: accessing to the form in our component

You can create a (ngSubmit) event binding that will be triggered when the form is submitted:

<form #datosJugador="ngForm" (ngSubmit)="onSubmit( datosJugador )">

At the same time in your component you have to program the onSubmit method:

  public onSubmit( datosJugador: NgForm ){

  }

Step 7 (optional): in the controls, use the attributes necesary

You can add standard html validation attributes like:

  • required
  • email: to tell angular that this field is an email
  • minlength: minimum length of the field
  • min: minimum value -if the field accepts numeric inputs-
  • max: maximum value -if the field accepts numeric inputs-

Step 8 (optional): you can still use two-way binding in your controls

<ion-col>
  <ion-checkbox name="jugadorEsPortero"
    labelPlacement="end" [(ngModel)]="jugadorEsPortero">
    <span *ngIf="jugadorEsPortero" style="color: red;">Si</span>
    <span *ngIf="!jugadorEsPortero" style="color: gray;">No</span> juega como portero</ion-checkbox>
</ion-col>

Step 9 (optional): or template variables, whatever suits you best

In this case, jugadorEsPortero will represent the internal control Angular makes for the form control. This way, you can check the value, or if it is valid, if it has been touched, etc.

<ion-col>
  <ion-checkbox name="jugadorEsPortero"
    labelPlacement="end" ngModel #jugadorEsPortero="ngModel">
    <span *ngIf="jugadorEsPortero.value" style="color: red;">Si</span>
    <span *ngIf="!jugadorEsPortero.value" style="color: gray;">No</span> juega como portero</ion-checkbox>
</ion-col>

Step 10: addign input validation

Angular sets many clases dynamically to help in notifiying the user if there are validation issues in the form's code:

  • ng-invalid: is assigned to every control that is in an invalid state
  • ng-valid: sets if the control becomes valid
  • ng-untouched: when the control haven't been touched by the user yet
  • ng-touched: everytime the control is touched
  • ng-pristine: the control is like the first time showed (this class dissapears when the control is touched first)
  • ng-dirty

We can use these classes to show visual aids to the user when a control becames invalid.

 <ion-input name="jugadorName"
 id="jugadorName"
 #jugadorName="ngModel"
 placeholder="Nombre"
 autofocus="true"
 inputmode="text"
 autocorrect="off"
 type="text" 
 ngModel
 required></ion-input>
 <ion-label for="jugadorName"
  class="validation-helper"
  *ngIf="jugadorName.touched && jugadorName.invalid">
  Jugador es obligatorio</ion-label>

In the css class I've defined the validation-helper class as:

.validation-helper {
  color: var(--ion-color-danger);
  font-size: 0.75rem;
}

If you are planning to block the submit button in the case of some of the values are not valid, the best you can do is to show up under the save button specific messages about which of the controls are not valid:

 @if(datosJugador.touched && !datosJugador.valid) {
  <ion-row>
    <ion-col>
      <label for="btnSave" class="validation-helper">Algunos datos no son correctos:</label>
    </ion-col>
   </ion-row>
 }
 @if( jugadorName.touched && jugadorName.invalid ){
  <ion-row>
    <ion-col>
      <label class="validation-helper">El nombre del jugador es un dato obligatorio</label>
    </ion-col>
   </ion-row>  
 }

Note: is convenient to add a more "generic" validation message also in the case that we have missing some of the validations: the user will still see a message "some data is incorrect" that gives it a hint about he problem despite our omission.

Step 11: accessing the form data programmatically

Given that we've named our form as datosJugador:

<form #datosJugador="ngForm" (ngSubmit)="onSubmit( datosJugador )">

We can access programmatically to them by initializing it as viewchild:

private datosJugador = viewChild<NgForm>('datosJugador');

And then, we can use to set values:

  public ngOnInit(): void {
    console.log("on init");
    setTimeout( () => {
      this.datosJugador().setValue({
        "jugadorName": "raul",
        "jugadorSurname": "luna", 
        "jugadorGenero": "Masculino", 
        "jugadorEdad": 15,
        "jugadorEsPortero": false, 
        "jugadorCategoria": ["Alevin"],
        "jugadorTelefono": "", 
        "jugadorTipoTelefono": "Personal", 
        "jugadorEmail": null, 
        "jugadorNotas": null });
    }, 1 );
  }

The call to setTimeout is needed because when calling the first time the initialization, the form isn't fully initialized, but if it is called as a callback function after just one millisecond, will solve the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment