了解Angular Pipes和数据绑定

129 阅读5分钟

在本教程中,我们将继续构建我们的游戏查看器应用程序,并在过程中添加新的功能。我们将研究如何使用属性数据绑定在我们的组件中设置图像属性,然后在模板中使用它们来为每个游戏显示图像。从那里我们将使用事件绑定来允许用户点击一个按钮,使图像消失,然后再出现。之后,我们将用ngModel和一个输入元素回顾双向绑定。最后,我们将看到Angular中的管道如何被用来转换绑定的数据。


显示图片的属性绑定

在组件中,我们可以使用属性绑定来设置图片的宽度,以及我们将为游戏显示的图片的边距。
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"
        }
    ];
}

然后在相关的模板文件中,可以使用[attributeename]='attributeevalue'语法访问这些类属性。注意,src和title属性来自类属性,而宽度和margin则来自游戏阵列中的json数据。
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>

好了,我们现在有了游戏的图片
angular property binding for css


事件绑定以响应按钮的点击

回顾一下,事件绑定是监听DOM中发生的事件,然后在事件发生时向组件代码发送消息的行为。在组件中,我们可以设置一个属性为 showImage的属性,并将其设置为真或假,以显示或不显示图像。默认情况下,它将被显示,所以它被设置为true。此外,我们还添加了 toggleImage()方法,如果没有图像,就简单地显示出来,如果已经存在,就删除它。

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;
    }
}

模板现在将有一个通过*(click)='toggleImage()'注册的点击然后一个简单的三元运算符将根据是否有图像存在而改变按钮内的文本,{{showImage ?'隐藏':'显示'}}图像*。三元运算符就像是if语句的一个快捷方式。这里的代码说,如果图像存在,则显示 "隐藏"。如果图像不存在,则显示 "显示"。我们把它和我们最近学到的*ngIf结构指令结合起来使用,以动态地添加或删除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 ' : '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>

下面是我们得到的结果。


双向绑定

Angular使用一种被称为Banana In A Box的语法来表示一种双向的绑定。因为这种语法,它被命名为Banana In A Box。 [()].我们将使用这种语法在文本输入上设置双向数据绑定,这将用于过滤游戏列表。目前,我们还没有设置过滤功能,但我们可以看到下面的双向绑定是如何工作的。首先,我们需要在app.module.ts中导入FormsModule,否则会出现错误,比如不能绑定到ngModel,因为它不是一个已知的属性输入

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 {
}

在组件中,我们添加一个新的类属性为 listItem.这将被绑定到模板中的文本输入,这样当模板中的值发生变化时,类中的值也会更新。
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;
    }
}

在模板中,该属性现在使用*[(ngModel)]="listItem "来绑定。*此外,我们又很好地利用了那个三元操作符来改变输入框旁边的文本是显示 "Filtered By "还是 "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 ' : '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>

你可以看到,如果输入框里有一个过滤词,那么文本就会显示 "Filtered By"。然而,如果我们把过滤词删除到没有字符,现在显示为 "未过滤"。

棱镜货币管道

管子是Angular中一个很好的功能。管道提供了一种方法来转换Angular模板中的绑定数据。有几个内置的管道,如果你喜欢,你也可以建立自定义的管道。一个管道接收一个值然后返回一个值。最常见的用途是对数据进行简单的转换。首先,我们来看看内置的货币管道,用它来格式化我们游戏的价格。你会看到语法非常简单。

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小写字母管道

另一个整洁的管道是小写字母管道。下面,我们将添加它,使每个游戏的所有零件编号或游戏代码从大写格式变成小写格式。

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


总结

当我们添加功能时,我们使用了Angular中四种不同的绑定类型,即插值、属性绑定、事件绑定和双向绑定。下面的图形显示了DOM(模板)和组件(类)之间每种类型的绑定方向。
four types of angular data binding

如果组件类中的数据与模板中的显示格式不同,那么你可以利用管道来进行数据转换。这通常会使用户的显示更加友好。

  • 管状字符 |是用来使用管道的。
  • 管子的名字是必须的,比如说 lowercase
  • 管道参数用冒号分隔。请看上面的货币例子,看看它的作用。