Introducing template-driven forms

Angular provides you with two different ways to add forms to an application. A very easy way of creating forms is to use templates, which Angular calls template-driven forms. By using these templates, we can add some directives to form elements as well as functionality such as value accessing and validations. In this section, we will be creating one of these forms. Let's get started:

  1. Let's add a simple HTML form so that we can add/edit the flashcards at the top of the app.component.html file: 
<form>
<h1 class="is-size-3">Add New Flash Card</h1>
<div class="field">
<label class="label" for="">Question</label>
<div class="control">
<input required class="input" type="text"
name="question">
</div>
</div>
<div class="field">
<label for="" class="label">Answer</label>
<div class="control">
<input required class="input" type="text"
name="answer">
</div>
</div>

<button class="button is-primary" type="submit">Submit</button>
<button class="button clear-btn">Clear</button>
</form>
  1. Then, we will add some CSS in the app.component.css file:
form {
margin: 10px;
width: 300px;
}

.clear-btn {
margin-left: 10px;
}

As you can see, we have added two fields: one for the question and another for the answer. We also have two buttons at the end of the form: one for submitting the form and one for clearing it.

  1. Now, we need to be able to get the data that we enter in the form back to our component. Angular provides a directive called ngModel to get data into the input fields. Since Angular uses one-way data binding, in order to get data out of the input field when you update the value in the DOM, we need output such as keyup or change:
<input required class="input" type="text" name="question" [ngModel]="flash.question" (keyup)="handleQuestionKeyup()">
  1. Instead of adding both input and output for control, Angular provides a syntax called Banana in a box, which does both things at once:
<input required class="input" type="text" name="question" [(ngModel)]="flash.question">

Now, whenever you change the value in the input field, the flash.question property is automatically updated. This syntax makes it look like Angular supports two-way data binding, like in Angular (the previous framework before Angular was created by the Angular team). 

  1. ngModel also provides additional functionality. It not only binds our controller properties to the fields but also stores the state of the input field. As you can see, our input field has a required attribute, which adds a validation rule that states that this field shouldn't be left empty. To get information regarding whether the input field is valid or not, we can use access input using a local variable, which can be created by using #variable in the template itself, as follows:
<input required class="input" type="text" name="question" [(ngModel)]="flash.question" #question="ngModel" [ngClass]="{'is-danger': quesiton.invalid && !question.pristine}">

In the preceding code, we added a local variable called question by using # and assigning the value to ngModel. Now, the question variable has the state of the input field.

We also added a directive called ngClass, which is used to dynamically add classes to our elements. In this example, we have passed an object with one property; that is, is-danger. Here, the is-danger class will be added if the value of question.invalid && !question.pristine is true; that is, if the question field is invalid and if it's not pristine (that is, the user hasn't changed the value since it was displayed in this form).

  1. Let's wire this thing up in our form:
<form #flashForm="ngForm">
<h1 class="is-size-3">Add New Flash Card</h1>
<div class="field">
<label class="label" for="">Question</label>
<div class="control">
<input required class="input" type="text"
name="question" #question [(ngModel)]="flash.question"
[ngClass]="{'is-danger': question.invalid &&
!question.pristine}"
>
</div>
</div>
<div class="field">
<label for="" class="label">Answer</label>
<div class="control">
<input required class="input" type="text" name="answer"
#answer [(ngModel)]="flash.answer" [ngClass]="{'is-
danger': answer.invalid && !answer.pristine}"
>
</div>
</div>

<button class="button is-primary" type="submit"
(click)="handleSubmit()"
>Submit</button>
<button class="button clear-btn"
(click)="handleClear()"
>Clear</button>
</form>

Along with adding ngModel and ngClass, we have added click events to our Submit and Clear buttons. We have added a local variable on the form element called flashForm and assigned it as ngForm. This means that the flashForm variable has the status of the whole form.

While developing, whenever you want to check a value quickly, we can use <pre>{{ flashForm.value | json }} </pre>. The json pipe is used to display objects in the UI, and the pre tag shows it in a nice format in the browser.

The form will look as follows in the browser:

  1. Now, let's wire up the logic of the submit buttons in AppComponent:
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
...
})
export class AppComponent {
@ViewChild('flashForm', {static:true}) flashForm: NgForm;
...

handleSubmit
(): void {
this.flashs.push({
id: generateId(),
...this.flash,
})

this.handleClear();
}

handleClear() {
this.flash = {
question: '',
answer: '',
};
this.flashForm.reset();
}
}

We have used ViewChild to query and to get access to the local variable flashForm, that we created in the template.

  1. Let's wrap up this section by adding the edit functionality, which we always want to add once we have the form in place. Our handleEdit method is changing the editing property's value to true. We will use that in our HTML file to update the form title from Add New Flash Card to Update Flash Card, and we'll use the Update and Cancel buttons instead of the Submit and Clear buttons so that we can update the flash and cancel the update:

<form #flashForm="ngForm">
<h1 class="is-size-3">{{editing ? 'Update' : 'Add New'}}
Flash Card
</h1>
<div class="field">
<label class="label" for="">Question</label>
<div class="control">
<input required class="input" type="text"
name="question" #question[(ngModel)]="flash.question"
[ngClass]="{'is-danger': question.invalid &&
!question.pristine}"
>
</div>
</div>
<div class="field">
<label for="" class="label">Answer</label>
<div class="control">
<input required class="input" type="text" name="answer"
#answer [(ngModel)]="flash.answer" [ngClass]="{'is-
danger': answer.invalid && !answer.pristine}"
>
</div>
</div>
<button *ngIf="editing; else submitBtn" class="button is-
primary" type="submit"
(click)="handleUpdate()">Update</button>
<ng-template #submitBtn>

<button class="button is-primary" type="submit"
(click)="handleSubmit()"
>Submit</button>
</ng-template>
<button *ngIf="editing; else clearBtn" class="button clear-btn
(click)="handleCancel()">Cancel</button>
<ng-template #clearBtn>

<button class="button clear-btn"
(click)="handleClear()"
>Clear</button>
</ng-template>
</form>

Now, let's click on Edit on one of the existing flashcard. You should see that the form is updated, as shown:

  1. Now, let's add the definitions for the handleEdit, handleUpdate, and handleCancel methods that we are using in the template:
@Component({
...
})
export class AppComponent {

handleEdit
(id: number): void {
this.editing = true;
this.editingId = id;
const flash = this.flashs.find(flash => flash.id === id);
this.flash.question = flash.question;
this.flash.answer = flash.answer;

}

handleUpdate() {
const flash = this.flashs.find(flash => flash.id ===
this.editingId);

flash.question = this.flash.question;
flash.answer = this.flash.answer;
this.handleCancel();
}

handleCancel() {
this.editing = false;
this.editingId = undefined;
this.handleClear();
}

}
  1. Let's now disable the Submit button and the Update button if the form is invalid, using the disabled attribute as follows:
<button *ngIf="editing; else submitBtn" class="button is-primary" type="submit" (click)="handleUpdate()" [disabled]="flashForm.invalid">Update</button>
<ng-template #submitBtn>

<button class="button is-primary" type="submit" (click)="handleSubmit()" [disabled]="flashForm.invalid">Submit</button>
</ng-template>

Now when the form is invalid, you should see that the Submit or the Update button will be disabled, as shown:

Now the user will be able to add a new flashcard and edit the existing flashcard.

This should complete our simple application. In the next two sections, we will try to improve our application by removing all the logic from AppComponent and adding the login to service in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.86.134