To Do app with Angular Forms - part 2
Tags: angular, javascript, webdev, beginners
Published: 2022-02-23 14:36:04

We can already see the form data in the console in our [application](https://www.vitainbeta.org/to-do-app-using-angular-forms-part-1/), and we know that there are other ways to pass data in Angular
- [Pass data with property binding](https://www.vitainbeta.org/how-to-pass-data-property-to-child-components-in-angular/)
- [Passing data with Angular Services](https://dev.to/this-is-angular/passing-data-with-angular-services-4mh)

However, in the context of Angular Forms we want to use FormsModule and [FormGroup](https://angular.io/api/forms/FormGroup) to "_tracks the value and validity state of a group of FormControl instances_".

## Basic Forms Validation

There are several ways to validate data coming from a form. We will start by using the `required` attribute in out `input` element.

```
<input 
  placeholder="Write a task" 
  ngModel 
  name="userInput" 
  required 
/>
``` 

According to MDN, the `required` attribute, "_if present, indicates that the user must specify a value for the input before the owning form can be submitted_". 

However, in our case, it fails miserably...
If you click on Add, it will always log something.

This is beacause of a default Angular behavior: "_By default, Angular disables native HTML form validation by adding the novalidate attribute on the enclosing form tag and uses directives to match these attributes with validator functions in the framework. If you want to use native validation in combination with Angular-based validation, you can re-enable it with the ngNativeValidate directive_".

Let's add the ngNativeValidate directive to the form tag and test the app.
  
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hh7k9mim8cug8m01i807.png)

It is not great, but it works fine.

## Display items

We need to add some code to our application to display the items added by the user.

Let's start from the template file, app.component.html.

Immediately under the form we add the following code:
```
// app.component.html

...

<ul>
  <li *ngFor="let item of todoList">{{ item }}</li>
</ul>
```

What does it do?

I am assuming you are familiar with [ul and li tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul). 

What is more interesting is that [ngFor stuff](https://angular.io/api/common/NgForOf). If you are not familiar with it, you could read the *ngFor syntax like this: For each item inside todoList, create a new `<li>` and add that item into the newly created `<li>`.

Where is _todoList_? We don't have it yet. But as the name suggest, todoList will contain all the items created by the user. Let's than add an array that we call _todoList_ into AppComponent.

```
// app.component.ts

...
export class AppComponent {
  userInput = '';
  todoList = ['Study Angular', 'Add one elememt', 'Correct typo'];

  onSubmit() { ... }
}
```

Let's modify onSubmit so that it concatenates the value of the userInput to the todoList array.


```
// app.component.ts

...
onSubmit() {
    this.todoList = this.todoList.concat(String(form.form.value.userInput));
  }
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h87c3afpa23erl20ekfq.png)
 
## General improvements

I will add a few lines of code to achieve the following outcomes:
- todoList becomes an array of objects
- each object in todoList contains a unique id, a task, and an optional date
- it is possible to delete the items in the UI

```
// app.component.ts

...
export class AppComponent {
  title = 'Ng To Do';
  userInput: string;
  dateInput: string;
  todoList: { id: number; title: string; date?: string }[] = [
    { id: 1, title: 'Study Angular' },
    { id: 2, title: 'Add one elememt' },
    { id: 3, title: 'Correct typo' },
    { id: 4, title: 'Add dates', date: '2022-09-10' },
  ];

  onSubmit(form: NgForm) {
    this.todoList = this.todoList.concat({
      id: Math.random(),
      title: form.form.value.userInput,
      date: form.form.value.date,
    });
    console.log('Submitted', form.form.value);
  }

  onDelete(id: number) {
    this.todoList = this.todoList.filter((item) => item.id !== id);
  }
}
```

This is not necessarily the best way to handle forms. We will soon start to group our controls.

Notice that todoList has a type: `{ id: number; title: string; date?: string }[]`. The type is an array of objects where each object must includes an id and a title. By appending the question mark to the date property `date?` we make the property optional.

Inside onSubmit we create a new object with the values from the UI. Then, we concatenate the object to todoList.

The onDelete method takes an id parameter of type number to delete the item associated with that id.

Our template changes as follow

```
// app.component.html

<h1>{{ title }}</h1>

<form (ngSubmit)="onSubmit(myForm)" #myForm="ngForm" ngNativeValidate>
  <label for="userInput">Add Task</label>
  <input placeholder="Write a task" ngModel name="userInput" required />
  <label for="date">By when</label>
  <input type="date" name="date" ngModel />
  <button type="submit">Add</button>
</form>

<ul>
  <li *ngFor="let item of todoList">
    <button (click)="onDelete(item.id)">X</button>
    {{ item.title }} {{ item.date && 'by' }} {{ item.date ? item.date : '' }}
  </li>
</ul>
```


We added a button for each `li` element. Clicking on the button triggers the onDelete method and passes the id of the element to delete.

The remaining `{{ item.date && 'by' }} {{ item.date ? item.date : '' }}` adds some code to show different ways to handle data conditionally. 

In JavaScript, the [logical AND (&&)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND) creates a condition so that it displays the value on the right side of the && only if the condition is true.

The [conditional (ternary) operator](https://javascript.plainenglish.io/javascript-ternary-operator-b76a02c90850) is another way to handle data conditionally.

## Grouping Form Controls

It is worth mentioning that Angular Forms offers the possibility to group controls. Grouping controls might be useful to group information in categories like user profile data, user preferences, etc.

Since our form is small, we add a description input and a label.

We then wrap all the elements related to userInput and taskDescription in a div tag. We add `ngModelGroup="taskInfo"` to the div to group the elements in it. 

```
// app.component.html

...
<div ngModelGroup="taskInfo">
    <label for="userInput">Add Task</label>
    <input placeholder="Write a task" ngModel name="userInput" required />
    <label for="taskDescription">Description</label>
    <input
      placeholder="Steps to complete the task"
      ngModel
      name="taskDescription"
    />
</div>
```
We can see the outcome by logging the value object of the form.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z45x9nc8ofsf8ub2c8k2.png)

Angular generated a taskInfo field that is another object containing the values of userInput and taskDescription.

You can see a similar field in the controls. That is pretty cool because it has all the properties of the controls in the group. This means that we could apply form checks, like `touched` or `dirty`, on the group of elements as a whole.

Conditional to the group being `touched` you could render some elements.

## Wrap Up
To use Angular Forms you need to:
1. **Import FormsModule** in AppModule
2. **Use the form tag** to wrap all form elements
3. **Declare controls**: Declare each control by adding `ngModel` and the name of the control
4. **Expose form object**: Set a local reference equal to ngForm in the form tag `#myForm="ngForm"`
5. **Submit**: Submit the form to pass data to the class. You can use event binding `(ngSubmit)="onSubmit(myForm)"`
6. **Group controls**: You may want to group elements by category. Use `ngModelGroup="group-name"` to wrap the elements you want to group.