Create a simple CRUD app

Angular 15

Display a list of items

In this tutorial you will learn how to work with data and collections:

you'll display a list of elements on screen by using the *ngFor directive, apply dynamic styles with ngClass directives, create custom TypeScript types and remove elements from the list.

2022-12-28
10 min read
Difficulty
angular
typescript

TOPICS

Goal

In this second part of the tutorial you will learn how to work with data and collections and you'll display a list of elements on screen by using the *ngFor directive.

You will also apply dynamic styles with ngClass directive, create custom TypeScript types and remove elements from the list.

What is a directive?

An Angular directive is a sort of custom HTML attribute that handle some logic behind the scenes.


For example you can use a directive to hide or show an element:

AngularHTML
<div *ngIf="condition">Some content</div>

Dinamically apply CSS classes

AngularHTML
<div [ngClass]="{'customCSSclass': condition}">...

Clone a part of the template looping thorgh array

AngularHTML
<div *ngFor="let item of items">{{item.prop}}</div>

You can also create your own custom directives.
For example, the following one may hide the HTML element if user is not logged

AngularHTML
<button *ifLogged>Some content</button>

Create the User type

Angular in written in TypeScript and we want that our code is strongly typed too. We have to display a list of "users" so, first, we have to create a custom type to represent the User entity.

Create a new src/app/model folder and add a file named user.ts into it. The complete path is src/app/model/user.ts:

The following interface allow you to create a new custom type that represents the structure of the User entity:

JavaScriptAngular
src/app/model/user.ts
export interface User {
  id: number;
  label: string;
  gender: string;
  age: number;
}

Interpolation and Pipes

Open app.component.ts, create an array of users and display its content in the template.

Interpolation (aka Template Tags)

Interpolation allow you to embed expressions into your HTML templates:

  <div>{{1+1}}</div>                  <!-- output: 2 -->
  <div>{{yourname}}</div>             <!-- output: the value of yourname  -->
  <div>{{condition ? x : y }}</div>   <!-- output: x or y in according with the condition -->
AngularTypeScript
app.component.ts
import { Component } from '@angular/core';
import { User } from './model/user';

@Component({
  selector: 'app-root',
  template: `
    <h4>Users</h4>
    <pre>{{users | json}}</pre>
  `,
  styles: [``]
})
export class AppComponent {
  users: User[] = [                                 // list of users
    { id: 1, label: 'Fabio', gender: 'M', age: 20 },
    { id: 2, label: 'Lorenzo', gender: 'M', age: 37 },
    { id: 3, label: 'Silvia', gender: 'F', age: 70 },
  ];
}

What is the `json` pipe?

Angular pipes are sort of formatters. There are several built-in pipes in angular to format dates, numbers, currencies and many others. Furthermore you can also create your own (sync and async) custom pipes.

The json pipe allow you to display (stringify) the content of objects and arrays, since you won't be able to see their content by default.

You can also try to remove the json pipe and see what's happen: <pre>{{users}}</pre>.

Why do i use inline HTML templates?

I'm using inline templates, instead of external templates, just to avoid opening too many files during this workshop. You may also create a separated file for your HTML template and another one for your component's CSS but I usually prefer this way in real world projects too in order to decrease the amount of files.

Read more about this in the previous chapter

TRY IT

Open your browser on http://localhost:4200 and see the result:

You should just display the following output:

ngFor: display a list of users

In this recipe you display a list of users:

You can use the *ngFor directive that simply clones the template in which it's applied for each item of an array.

So you can replace the previous HTML template with the following one:

AngularHTML
app.component.ts
<div class="container">
  <h1>Users</h1>
  <ul class="list-group">
    <li *ngFor="let u of users" class="list-group-item">
      {{u.label}}
    </li>
  </ul>
</div>

You should see the following result:

Get more info about the ngFor directive reading the official documentation

ngClass: dynamic CSS icons

Our goal is now display a different font-awesome icon to represent the user's gender, close to its name.

The syntax to display a font-awesome icon is the following:

HTML
<i class="fa fa-[ICON]">        <!-- Any icon-->
<i class="fa fa-google">        <!-- Google icon -->
<i class="fa fa-mars">          <!-- Man Icon -->
<i class="fa fa-venus">         <!-- Woman Icon -->
<i class="fa fa-3x fa-mars">   <!-- Increase size -->
<i class="fa fa-3x fa-venus">   <!-- Increase size -->

So we should simply shown an icon close to the user's name:

fa fa-mars for men, fa fa-venus for women and so on...

We might create two HTML elements, for each icon:

AngularHTML
app.component.ts
<li *ngFor="let u of users" class="list-group-item">
  <i class="fa fa-3x fa-mars" *ngIf="u.gender === 'M'"></i>
  <i class="fa fa-3x fa-venus" *ngIf="u.gender === 'F'"></i>
  
  {{u.label}}
</li>

Anyway, you may have noticed that the classes fa fa-3x are the same for each icon, while the last part changes. So we can use this trick:

  • we create just one icon instead of a different icon for each gender;
  • we statically apply fa fa-3x to each icon by using the HTML class attribute;
  • we use the ngClass directive to add a different class in according with the condition.
AngularHTML
app.component.ts
 <div class="container">
    <h1>Users</h1>
    <ul class="list-group">
      <li *ngFor="let u of users" class="list-group-item">
        <i
          class="fa fa-3x"
          [ngClass]="{
          'fa-mars': u.gender === 'M',
          'fa-venus': u.gender === 'F'
        }"
        ></i>
        
        {{u.label}}
      </li>
    </ul>
  </div>

Your application should now display the properly icon close to the user's name, as shown in the image above.

Custom CSS

Sometimes the CSS frameworks you have included in your projects does not fully meet your needs and you have to create your own custom classes.

GOAL

In this example we just want to set a different background color for women and men.
I'm sorry for the stereotypes. It's just an example

The main ways to achieve this goal are the following two:

  1. GLOBAL CSS: add your custom classes into src/styles.css, so your CSS will be globally available throughout your application. Anyway the risk of having conflicts with other classes is very high.

  2. LOCAL CSS (encapsulated): add your classes directly into your components so they will only be available in the component that have defined them.

Whenever possible I would use the second strategy, since it is the least risky.

So add following CSS classes in the styles property of app.component:

AngularCSS
app.component.ts
styles: [`
  .male { background-color: #36caff; }
  .female { background-color: pink; }
`]

You may also use an external CSS file by using the following syntax:

styleUrls: [`./app.component.css`]

And add the ngClass directive to the HTML element that contain the *ngFor:

AngularHTML
app.component.ts
  <!-- missing part -->
  <li
    *ngFor="let u of users" class="list-group-item"
    [ngClass]="{
      'male': u.gender === 'M', 
      'female': u.gender === 'F'
    }"
  >
  <!-- missing part -->           

COMPLETED SOURCE CODE

So far, your app.component.ts file should look like the following:

Angular
app.component.ts
import { Component } from '@angular/core';
import { User } from './model/user';

@Component({
  selector: 'app-root',
  template: `
    <div class="container">
      <h1>Users</h1>
      <ul class="list-group">
        <li
          *ngFor="let u of users" class="list-group-item"
          [ngClass]="{
            'male': u.gender === 'M', 
            'female': u.gender === 'F'
          }"
        >
          <i
            class="fa fa-3x"
            [ngClass]="{
            'fa-mars': u.gender === 'M',
            'fa-venus': u.gender === 'F'
          }"
          ></i>
          
          {{u.label}}
        </li>
      </ul>
    </div>
  `,
  styles: [`
    .male { background-color: #36caff; }
    .female { background-color: pink; }
  `]
})
export class AppComponent {
  users: User[] = [
    { id: 1, label: 'Fabio', gender: 'M', age: 20 },
    { id: 2, label: 'Lorenzo', gender: 'M', age: 37 },
    { id: 3, label: 'Silvia', gender: 'F', age: 70 },
  ];
}

Remove an Item

In this recipe you will add a "TRASH" icon to each list item in order to delete a specific user.

  1. we have to display a "Trash" icon in app.component.ts in each list item (aligned to the right side)
  2. we have to set a click event listener to each icon and invoke the (new) deleteHandler method, passing the user to remove as paremeter.
AngularHTML
app.component.ts
<!-- missing part -->
{{u.label}}
<i class="fa fa-trash fa-2x pull-right" (click)="deleteHandler(u)"></i>
<!-- missing part -->

You may also pass the id only instead of the whole User object

Add the new deleteHandler() method to app.component class.
This method simply use the filter array method to create a new collection with all items except the element to remove.

AngularTypeScript
app.component.ts
deleteHandler(userToRemove: User) {
  this.users = this.users.filter(u => u.id !== userToRemove.id);
}

IMMUTABILITY

Why did I filter the users array by creating a new reference?

I may simply use the following strategy to accomplish the same task:

const index = this.users.findIndex(u => u.id === userToRemove.id);
this.users.splice(index, 1); // mutate the array removing an element

I'm using what is called "Immutability".

In fact I don't mutate data (users, in our example) but I have completely created a new array reference by using the filter array method (that generates a new array reference)

But why?

Currently there are no differences between the two approaches but you may need to optimize performance in the future or use an Angular feature that requires this approach. So I think it is important to get an idea of its existence in order to be able to explore it independently.

Final source code

AngularTypeScript
app.component.ts
import { Component } from '@angular/core';
import { User } from './model/user';

@Component({
  selector: 'app-root',
  template: `
    <div class="container">
      <h1>Users</h1>
      <ul class="list-group">
        <li
          *ngFor="let u of users" class="list-group-item"
          [ngClass]="{
            'male': u.gender === 'M', 
            'female': u.gender === 'F'
          }"
        >
          <i
            class="fa fa-3x"
            [ngClass]="{
            'fa-mars': u.gender === 'M',
            'fa-venus': u.gender === 'F'
          }"
          ></i>
          
          {{u.label}}

          <i class="fa fa-trash fa-2x pull-right" (click)="deleteHandler(u)"></i>
        </li>
      </ul>
    </div>
  `,
  styles: [`
    .male { background-color: #36caff; }
    .female { background-color: pink; }
  `]
})
export class AppComponent {
  users: User[] = [                                 // list of users
    { id: 1, label: 'Fabio', gender: 'M', age: 20 },
    { id: 2, label: 'Lorenzo', gender: 'M', age: 37 },
    { id: 3, label: 'Silvia', gender: 'F', age: 70 },
  ];

  deleteHandler(userToRemove: User) {
    this.users = this.users.filter(u => u.id !== userToRemove.id);
  }
}

TRY IT

You should now able to remove elements from the list.

Try it on http://localhost:4200 or use the interactive code playground below:

WHAT'S NEXT

ADS: MY LATEST VIDEO COURSE <br />(italian only)ADS: MY LATEST VIDEO COURSE <br />(italian only)
ADS: MY LATEST VIDEO COURSE
(italian only)

Keep updated about latest content
videos, articles, tips and news
BETA