Angular Pipes And Data Binding

angular pipes and data binding

In this tutorial, we’ll continue to build our games viewer application and add new features as we go. We’re going to look at how we can use property data binding to set image properties in our component, then use them in the template to display an image for each game. From there we’ll use event binding to allow the user to click a button that will make the image disappear and then reappear. After that, we’ll review two-way binding with ngModel and an input element. Finally, we’ll see how Pipes in Angular can be used to transform bound data.


Property Binding To Display an Image

In the component we can use property binding to set an image width, as well as the margin for the image we will display for our games.
game-list.component.ts

import {Component} from '@angular/core';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html'
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    games = [
        {
            "gameId": 1,
            "gameName": "Castlevania",
            "gameCode": "AAA-0101",
            "releaseDate": "September 26, 1986",
            "description": "Action-adventure game series created and developed by Konami.",
            "price": 19.99,
            "thumbRating": 5.0,
            "imageUrl": "./assets/images/castlevania.png"
        },
        {
            "gameId": 2,
            "gameName": "Dr Mario",
            "gameCode": "AAA-1100",
            "releaseDate": "July 27, 1990",
            "description": "Action puzzle game produced by Gunpei Yokoi and published by Nintendo.",
            "price": 15.99,
            "thumbRating": 3,
            "imageUrl": "./assets/images/drmario.png"
        },
        {
            "gameId": 3,
            "gameName": "Kid Icarus",
            "gameCode": "AAA-0048",
            "releaseDate": "December 19, 1986",
            "description": "Kid Icarus revolves around protagonist Pit's quest for three sacred treasures.",
            "price": 20.99,
            "thumbRating": 4,
            "imageUrl": "./assets/images/kidicarus.png"
        },
        {
            "gameId": 4,
            "gameName": "Legend Of Zelda",
            "gameCode": "AAA-0049",
            "releaseDate": "February 21, 1986",
            "description": "Link is often given the task of rescuing Princess Zelda and the kingdom of Hyrule from Ganon.",
            "price": 29.95,
            "thumbRating": 5,
            "imageUrl": "./assets/images/legendofzelda.png"
        },
        {
            "gameId": 7,
            "gameName": "Super Mario Bros",
            "gameCode": "AAA-3325",
            "releaseDate": "September 13, 1985",
            "description": "Mario finds power-ups and items that give him special magic powers such as fireball-throwing and size-changing into giant and miniature sizes.",
            "price": 40.95,
            "thumbRating": 5,
            "imageUrl": "./assets/images/supermariobros.png"
        }
    ];
}

Then in the associated template file, those class properties can be accessed using the [attributename]=’attributevalue’ syntax. Note that the src and title attributes are coming from the class properties, while the width and margin are coming from that json data in the games array.
game-list.component.html

<div class='card'>
    <div class='card-header'>
        {{pageTitle}}
    </div>
    <div class='card-body'>
        <div class="row">
            <div class="col-3">
                <input type="text" class="form-control" id="filterInput" placeholder="Type to filter">
            </div>
            <div class="col">
                <div class="form-text text-muted">Filtered by:</div>
            </div>
        </div>
        <div class='table-responsive'>
            <table class='table' *ngIf='games && games.length'>
                <thead>
                <tr>
                    <th>
                        <button class='btn btn-primary'>
                            Hide Image
                        </button>
                    </th>
                    <th>Game</th>
                    <th>Part#</th>
                    <th>Release Date</th>
                    <th>Cost</th>
                    <th>5 Thumb Rating</th>
                </tr>
                </thead>
                <tbody>
                <tr *ngFor='let game of games'>
                    <td>
                        <img [src]='game.imageUrl'
                             [title]='game.gameName'
                             [style.width.px]='imageWidth'
                             [style.margin.px]='imageMargin'>
                    </td>
                    <td>{{ game.gameName }}</td>
                    <td>{{ game.gameCode }}</td>
                    <td>{{ game.releaseDate }}</td>
                    <td>{{ game.price }}</td>
                    <td>{{ game.thumbRating }}</td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>
    <div class="card-footer text-muted text-right">
        <small>Powered by Angular</small>
    </div>
</div>

Ok we now have images for our games!
angular property binding for css


Event Binding To Respond To Button Click

Recall that event binding is the act of listening for an event to happen in the DOM, and then sending a message to the component code when the event fires. In the component, we can set up a property of showImage and set that to either true or false to display or not display the image. By default it will be displayed, so it is set to true. Additionally, we add the toggleImage() method which simply displays an image if there is none, or removes it if it is present already.

game-list.component.ts

import {Component} from '@angular/core';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html'
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    games = [...];

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}

The template will now have a click even registered via (click)=’toggleImage()’, and then a simple ternary operator will change the text inside the button based on if there is an image present or not with {{showImage ? ‘Hide’ : ‘Show’}} Image. A ternary operator is like a short cut for an if statement. The code here says if the image is present, then display “Hide”. If the image is not there, then display “Show”. We use this in combination with the *ngIf structural directive we recently learned about to dynamically add or remove the <img> element from the dom.

game-list.component.html

<div class='card'>
    <div class='card-header'>
        {{pageTitle}}
    </div>
    <div class='card-body'>
        <div class="row">
            <div class="col-3">
                <input type="text" class="form-control" id="filterInput" placeholder="Type to filter">
            </div>
            <div class="col">
                <div class="form-text text-muted">Filtered by:</div>
            </div>
        </div>
        <div class='table-responsive'>
            <table class='table' *ngIf='games && games.length'>
                <thead>
                <tr>
                    <th>
                        <button class='btn btn-primary'
                                (click)='toggleImage()'>
                            {{showImage ? 'Hide&nbsp;' : 'Show'}} Image
                        </button>
                    </th>
                    <th>Game</th>
                    <th>Part#</th>
                    <th>Release Date</th>
                    <th>Cost</th>
                    <th>5 Thumb Rating</th>
                </tr>
                </thead>
                <tbody>
                <tr *ngFor='let game of games'>
                    <td>
                        <img *ngIf='showImage'
                             [src]='game.imageUrl'
                             [title]='game.gameName'
                             [style.width.px]='imageWidth'
                             [style.margin.px]='imageMargin'>
                    </td>
                    <td>{{ game.gameName }}</td>
                    <td>{{ game.gameCode }}</td>
                    <td>{{ game.releaseDate }}</td>
                    <td>{{ game.price }}</td>
                    <td>{{ game.thumbRating }}</td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>
    <div class="card-footer text-muted text-right">
        <small>Powered by Angular</small>
    </div>
</div>

Here is the result we get.


Two Way Binding

Angular uses a syntax known as a Banana In A Box to signify a two way binding. It is named a Banana In A Box because of this syntax: [()]. We’ll use that syntax to set up two way data binding on the text input which will be used to filter the list of games. At the moment, we do not have filtering set up, but we can see how the two way binding works below. First we need to import the FormsModule in app.module.ts, otherwise there will be errors such as Can’t bind to ngModel since it isn’t a known property input.

app.module.ts

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';

import {AppComponent} from './app.component';
import {GameListComponent} from './games/game-list.component';

@NgModule({
    declarations: [
        AppComponent,
        GameListComponent
    ],
    imports: [
        BrowserModule,
        FormsModule
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

In the component, we add a new class property of listItem. This will be bound to the text input in the template so that when the value changes in the template, the value in the class will also update.
game-list.component.ts

import {Component} from '@angular/core';

@Component({
    selector: 'game-list',
    templateUrl: './game-list.component.html'
})
export class GameListComponent {
    pageTitle = 'Dynamic! Game List';
    imageWidth = 45;
    imageMargin = 1;
    showImage = true;
    listItem = 'Mario';
    games = [... ];

    toggleImage(): void {
        this.showImage = !this.showImage;
    }
}

In the template, that property is now bound using [(ngModel)]=”listItem”. In addition, we make good use of that ternary operator again to change whether the text next to the input box shows “Filtered By” or “Not Filtered”.

game-list.component.html

<div class='card'>
    <div class='card-header'>
        {{pageTitle}}
    </div>
    <div class='card-body'>
        <div class="row">
            <div class="col-3">
                <input [(ngModel)]="listItem" type="text" class="form-control" id="filterInput" placeholder="Type to filter">
            </div>
            <div class="col">
                <div class="form-text text-muted">
                    {{listItem ? 'Filtered By:' : 'Not Filtered'}} {{listItem}}
                </div>
            </div>
        </div>
        <div class='table-responsive'>
            <table class='table' *ngIf='games && games.length'>
                <thead>
                <tr>
                    <th>
                        <button class='btn btn-primary'
                                (click)='toggleImage()'>
                            {{showImage ? 'Hide&nbsp;' : 'Show'}} Image
                        </button>
                    </th>
                    <th>Game</th>
                    <th>Part#</th>
                    <th>Release Date</th>
                    <th>Cost</th>
                    <th>5 Thumb Rating</th>
                </tr>
                </thead>
                <tbody>
                <tr *ngFor='let game of games'>
                    <td>
                        <img *ngIf='showImage'
                             [src]='game.imageUrl'
                             [title]='game.gameName'
                             [style.width.px]='imageWidth'
                             [style.margin.px]='imageMargin'>
                    </td>
                    <td>{{ game.gameName }}</td>
                    <td>{{ game.gameCode }}</td>
                    <td>{{ game.releaseDate }}</td>
                    <td>{{ game.price }}</td>
                    <td>{{ game.thumbRating }}</td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>
    <div class="card-footer text-muted text-right">
        <small>Powered by Angular</small>
    </div>
</div>

You can see that if there is a filter term in the input, then the text displays “Filtered By”. However, if we remove the filter term to no characters, it now displays as “Not Filtered”.

Angular Currency Pipe

Pipes are a nice feature to make use of in Angular. Pipes provide a means to transform bound data in an Angular template. There are several built-in pipes, and you can also build custom pipes if you like. A pipe takes in a value then returns a value. The most common use is for simple transformations on data. First we’ll look at the built-in currency pipe and use it to format the price of our games. You’ll see the syntax is very simple.

game-list.component.html

<tr *ngFor='let game of games'>
    <td>
        <img *ngIf='showImage'
             [src]='game.imageUrl'
             [title]='game.gameName'
             [style.width.px]='imageWidth'
             [style.margin.px]='imageMargin'>
    </td>
    <td>{{ game.gameName }}</td>
    <td>{{ game.gameCode }}</td>
    <td>{{ game.releaseDate }}</td>
    <td>{{ game.price | currency:'USD':'symbol':'1.2-2' }}</td>
    <td>{{ game.thumbRating }}</td>
</tr>

angular currency pipe example


Angular lowercase Pipe

Another neat pipe is the lowercase pipe. Below, we’ll add it to make all of the part numbers or game codes of each game become transformed from an uppercase format to a lowercase format.

game-list.component.html

<tr *ngFor='let game of games'>
    <td>
        <img *ngIf='showImage'
             [src]='game.imageUrl'
             [title]='game.gameName'
             [style.width.px]='imageWidth'
             [style.margin.px]='imageMargin'>
    </td>
    <td>{{ game.gameName }}</td>
    <td>{{ game.gameCode | lowercase }}</td>
    <td>{{ game.releaseDate }}</td>
    <td>{{ game.price | currency:'USD':'symbol':'1.2-2' }}</td>
    <td>{{ game.thumbRating }}</td>
</tr>

angular lowercase pipe example


Summary

As we added features we worked with the four different types of binding in Angular which are Interpolation, Property Binding, Event Binding, and Two Way Binding. The graphic below shows the direction of each type of binding between the DOM (Template) and the Component (Class).
four types of angular data binding

If there is data in the component class which is not in the format needed for display in the template, then you can make use of Pipes to perform a data transformation. This often makes for a more user-friendly display to the user.

  • The Pipe character | is used to make use of a Pipe
  • The Pipe name is required, such as lowercase
  • Pipe parameters are separated with colons. Look at the currency example above to see this in action