Create a CRUD Application with Inline Table Editing in Angular 14 +
Tags: webdev, javascript, beginners, angular
Published: 2022-12-01 18:42:58

## Introduction

In this post, I will show you how to create a CRUD application in Angular, that allows you to create, update and delete records from the table itself. 

This project is suited for beginners.

You can find the repository for the project on my [GitHub] by clicking [here](https://github.com/chadwinjdeysel/inline-table-editor/blob/master/README.md).

Please note, this application will not be integrated with a database and is for demonstration purposes only. So let’s get started. 

## Initialize the project setup.

Firstly, we need to ensure that you have Nodejs, NPM and Angular installed. You can check this by opening a new terminal and running the following commands: 

```
node -v
```

```
npm -v
```

```
ng version
```

If you haven’t yet installed any of these, I would recommend you check out [this guide](https://angular.io/guide/setup-local).

Now let’s create our application. In your terminal, run the following command. 

```
ng new inline-table-editor
```

Then, navigate into your project and open up the file in your code editor of choice.

Now that the application has been created, we’ll need to install and set up the UI library which in this case is going to Bootstrap. Install NGXBootstrap:

```
ng add ngx-bootstrap
```

Next, we’ll need to add the Bootstrap & Bootstrap Icons CSS libraries into the application. To do so, navigate to the index.html page, and in the `<head>` section add the following code: 

```html
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css">

<!-- Bootstrap Icons CSS --> 
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
```

With that, the project is ready to go. 

## Create the Users table.

Let’s get started by creating the user's table. First, we’ll need to define our data model. In the root of the src folder, create a new folder names models. And add a new file titled: “Users.ts”. In the Users file add the following code to define the Users interface: 

```tsx
export interface User {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
}
```

Next, we’ll seed some demo data into the table. To generate some demo data, you can checkout [JSON Generator](https://json-generator.com/). Once you’ve opened the window, replace the code in the left tab with the following code and hit generate.

```json
[
   '{{repeat(8)}}',
  {
    id: '{{objectId()}}',
    firstName: '{{firstName()}}',
  	lastName: '{{surname()}}',
  	email: '{{email()}}',
  }
]
```

This will generate 8 random objects that can then be copied to your clipboard. Then navigate back to our application and in `app.component.ts` file, create a new variable and paste your mock data. 

```tsx
user: User[] = [
	// paste your generated content here.
]
```

It’s worth pointing out that in real-life applications, the data would be stored in a database and retrieved once needed using an API. 

Now with that completed, navigate to the `app.component.html` file and replace all the existing code with the following to create the table. 

```html
<div class="container">
	<table class="table">
		<thead>
        <tr>
          <th scope="col">Id</th>
          <th scope="col">First Name</th>
          <th scope="col">Last Name</th>
          <th scope="col">Email Address</th>
          <th>
            <button
              type="button"
              title="Add New User"
              class="btn btn-primary"
            >
              <i class="bi bi-plus"></i>
            </button>
          </th>
        </tr>
      </thead>

			<tbody>
        <ng-container *ngFor="let user of users; let i = index">
          <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.firstName }}</td>
            <td>{{ user.lastName }}</td>
            <td>{{ user.email }}</td>
            <td>
              <button
                class="btn btn-danger btn-sm mx-1"
                title="Delete User"
                type="button"
              >
                <i class="bi bi-trash"></i>
              </button>
            </td>
          </tr>
        </ng-container>
      </tbody>
	</table>
</div>
```

Once completed, run the application and navigate to [http://localhost:4200](http://localhost:4200), view your table with the demo data inside of it.

```
ng serve
```

## Updating an Existing User

To perform updates to the data, we’ll be making use of the Reactive Forms Module that’s built in angular. Firstly, add the `ReactiveFormsModule` to you application in the `app.module.ts` file.

```tsx
imports: [
    // Existing Modules
    ReactiveFormsModule,
], 
```

Next we’ll need to add a constructor to our `app.component.ts` file and inject the Form Builder, which is used to build our reactive forms. 

```tsx
constructor(
    private fb: FormBuilder
) { }
```

Now, we’ll create our form with the needed fields and validation requirements. 

```tsx
form = this.fb.group({
    firstName: ['', [Validators.required]],
    lastName: ['', [Validators.required]],
    email: ['', [Validators.required, Validators.email]]
 });
```

I’m planning on creating more content based on the Reactive Forms Module. Sign up to my newsletter for my latest updates.

With this application, you can update any user by just clicking on them. However, we want to make sure that the user is only able to make changes to once user at a time. To accomplish that, we’ll create a property, `userSelected` and a method called `selectUser()` to select a user to select the user that is too be updated.

```tsx
userSelected: User = {} as User;

selectUser(user: User) {
		// check if userSelected is empty, before assigning a selected user
    if(Object.keys(this.userSelected).length === 0) {
      this.userSelected = user;

      this.form.patchValue({
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email
      })
    }
  }
```

Next, we’ll need to let the UI respond to us selecting a user. For this I want the background color of the row to change and replace the table data with inputs. To achieve this, replace the code in the `<tbody>` with the following. 

```html
<tbody>
  <ng-container *ngFor="let user of users; let i = index">
    <tr
			*ngIf="userSelected != user"
			(click)="selectUser(user)"
		>
      <td>{{ user.id }}</td>
      <td>{{ user.firstName }}</td>
      <td>{{ user.lastName }}</td>
      <td>{{ user.email }}</td>
      <td>
        <button
          class="btn btn-danger btn-sm mx-1"
          title="Delete User"
          type="button"
        >
          <i class="bi bi-trash"></i>
        </button>
      </td>
    </tr>

		<tr *ngIf="userSelected == user" class="table-primary">
      <td>{{ user.id }}</td>
      <td>
	      <input
	        type="text"
	        class="form-control"
	        formControlName="firstName"
	        [value]="user.firstName"
	        title="First Name"
	        />
      </td>
      <td>
        <input
          type="text"
          class="form-control"
          formControlName="lastName"
          [value]="user.lastName"
          title="Last Name"
          />
      </td>
      <td>
        <input
          type="text"
          class="form-control"
          formControlName="email"
          [value]="user.email"
          title="Email Address"
          />
      </td>
      <td>             
        <button
            class="btn btn-primary btn-sm mx-1"
            title="update"
            type="submit"
            [disabled]="form.invalid"
          >
            <i class="bi bi-lightning"></i>
          </button>

          <button
            class="btn btn-danger btn-sm mx-1"
            title="Cancel Changes"
            type="button"
          >
            <i class="bi bi-x-lg"></i>
          </button>
        </td>
    </tr>
  </ng-container>
</tbody>
```

To summarize this code, it checks if the user selected is the user that is in the row of the table. If so, we change the background color of the row and add in the form inputs needed. It’s important to note that this code the form above will not work. To do that, we’ll need to wrap our table in a `<form>` tag. 

```html
<form [formGroup]="form" (ngSubmit)="update()">
	<table class="table">

	</table>
</form>
```

Now let’s create the `update()` to update the user. 

```tsx
update() {
	  // gets the index of the user we need to update
    let index = this.users.map(u => u.id).indexOf(this.userSelected.id);
		
		// updates the user at the index selected
    this.users[index] = {
      id: this.userSelected.id,
      firstName: this.form.value.firstName!,
      lastName: this.form.value.lastName!,
      email: this.form.value.email!
    };

		// clean up
		this.userSelected = {} as User;
    this.form.reset();  
}
```

Finally, we’ll need the “Cancel Changes” button to undo the changes and reset the UI.

```html
<!-- Cancel Changes Button in app.component.html -->
<button
  class="btn btn-danger btn-sm mx-1"
  title="Cancel Changes"
  type="button"
	(click)="cancel()"
>
	<i class="bi bi-x-lg"></i>
</button>
```

```tsx
// app.component.ts
cancel() {
	// clears the user selected
	this.userSelected = {} as User;
  
	// resets the form
  this.form.reset();
}
```

## Create a new User

To create a new user, we’ll use a lot of the same methods and functionality that we’ve already built in. Let’s start by adding the `isEditing` property. This property will distinguish between if we’re currently editing an existing user or adding a new one. Add the `isEditing` variable to the top of the `app.component.ts`.

```tsx
isEditing: boolean = false
```

Next, let’s make changes to our existing code to update the functionality.  Replace the existing methods in the `app.component.ts` with the code below. 

```tsx
selectUser(user: User) {
  if(Object.keys(this.userSelected).length === 0) {
    this.userSelected = user;
    this.isEditing = true

    this.form.patchValue({
      firstName: user.firstName,
      lastName: user.lastName,
      email: user.email
    })
  }
}

update() {
  if(!this.isEditing) {
    this.users[0] = {
      id: this.generateId(),
      firstName: this.form.value.firstName!,
      lastName: this.form.value.lastName!,
      email: this.form.value.email!
    }
  }   
  else {
    let index = this.users.map(u => u.id).indexOf(this.userSelected.id);

    this.users[index] = {
      id: this.userSelected.id,
      firstName: this.form.value.firstName!,
      lastName: this.form.value.lastName!,
      email: this.form.value.email!
    };
  }

  // clean up
  this.userSelected = {} as User;
  this.isEditing = false 
  this.form.reset();   
}

cancel() {
  if(!this.isEditing && confirm('All unsaved changes will be removed. Are you sure you want to cancel?')) {
		// removes the user that was added
    this.users.splice(0, 1);
  }

  this.userSelected = {} as User;
  this.isEditing = false
  this.form.reset();
}
```

We’ll also need to add another method to generate an ID for the user. In a real world scenario, this would be generated by the backend or database.

```tsx
// Generates a random string of characters.
generateId() {
  return (Math.random() + 1).toString(36).substring(4) + (Math.random() + 1).toString(36).substring(4) 
}
```

Finally, let’s create the `addUser()` to set add our new user to the frontend.

```html
<!-- Add New User button, app.component.html -->
<button
  type="button"
  title="Add New"
  class="btn btn-primary"
  (click)="addUser()"
>
  <i class="bi bi-plus"></i>
</button>
```

```tsx
// app.component.ts
addUser() {
  this.users.unshift({
    id: '-',
    firstName: '',
    lastName: '',
    email: ''
  })
  this.userSelected = this.users[0];
}
```

## Deleting a User

To finalize all the CRUD functionality, we’ll need to create a way to delete a user. 

```html
<!-- Delete User button, app.component.html -->
<button
  class="btn btn-danger btn-sm mx-1"
  title="delete"
  type="button"
  (click)="deleteUser(i)"
>
  <i class="bi bi-trash"></i>
</button>
```

```tsx
// app.component.ts
deleteUser(index: number) {
  if(confirm('Are you sure you want to delete this user?')) {
    this.users.splice(index, 1);
  }
}
```

## Conclusion

I hope you found this post useful.

Be sure to check me out on [Twitter](https://twitter.com/ChadwinJDeysel) for more Angular and development tips. Thanks for reading and have a great day! 😄