Tìm hiểu về pipe trong Angular 2

Nếu như bạn đã làm quen với AngularJS 1.x , thì khái niệm pipes Angular 2 có lẽ không có gì quá xa lạ. Nó chính là filters trong Angular 1, thậm chí đến tên gọi của một số filter cũng được thay tương ứng bằng pipes cùng tên trong Angular 2 ( trừ một số thay đổi nhỏ, có 3 filter đã biến mất trong Angular 2 là : number, orderBy và filter. Đồng thời có 3 pipes mới xuất hiện là : asynce, decimal và percent ) . Tuy nhiên, nếu bạn còn lạ lẫm với khái niệm này, hãy cùng tìm hiểu về pipes trong Angular 2.

Pipe và cách sử dụng

Hiểu một cách đơn giản thì pipes trong AngularJS là cách để ta thực hiện việc chuyển hóa dữ liệu ( transformation ), để dễ dàng hiển thị ra dưới định dạng như ta mong muốn. Chẳng hạn như trong ví dụ sau

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

@Component({
  selector: 'hero-birthday',
  template: `<p>The hero's birthday is {{ birthday }}</p>`
})
export class HeroBirthdayComponent {
  birthday = new Date(1988, 3, 15); // April 15, 1988
}

nếu để hiển thị ngày sinh bằng cách mặc định như thế này, output của ta sẽ có dạng ri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time) , tương đối khó đọc. Bằng cách dùng pipe date, build-in có sẵn trong Angular 2, ta có thể chuyển string hiển thị ngày tháng về dạng dễ đọc hơn

template: `<p>The hero's birthday is {{ birthday | date }}</p>`

Output của ta lúc này sẽ là April 15, 1988, dễ nhìn hơn rất nhiều.

Mặc định, trong Angular 2 đã build-in sẵn các pipes sau, để ta có thể ngay lập tức sử dungj: currency, date, uppercase, json, limitTo, lowercase, async, decimal, percent .

Như trong ví dụ bên trên, là cách cơ bản để sử dụng pipe . Nếu cần thiết, ta có thể truyền thêm tham số

    <p>The hero's birthday is {{ birthday | date:"shortDate" }}</p>

sẽ cho ra output 04/15/1988 , hay nếu ta dùng

    <p>The hero's birthday is {{ birthday | date:"fullDate" }}</p>

sẽ cho output Friday, April 15, 1988.

Ta cũng có thể chain ,gọi cùng lúc nhiều pipe

    <p>The hero's birthday is {{ birthday | date | uppercase }}</p>

cho ta output FRIDAY, APRIL 15, 1988

Custom Pipe

Đôi khi, chỉ sử dụng các pipe có sẵn là không đủ, tùy theo nhu cầu, ta có thể viết các custom pipe, thực hiện việc chuyển hóa như ý mình muốn. Ta có thể cùng tìm hiểu qua một ví dụ.

Một tình huống khá phổ biến, dễ gặp, đó là khi ta muốn duyệt một mảng, hay một object, theo từng cặp key-value của nó . Đây là một kịch bản khá thông dụng, và thường thì các ngôn ngữ hay framework đều có hỗ trợ sẵn cả. Ví dụ như trong PHP, đơn giản ta chỉ cần

    foreach ($arrayItems as $key => $value) {
        // Do something
    }

thế nhưng, khá ngạc nhiên là ngFor của Angular 2 lại không hỗ trợ sẵn cho tình huống này ( có vẻ là vì vấn để performance, nên Angular 2 không khuyến khích xử lí theo hướng này ??? ) . Tuy nhiên Ta có trong tay ngFor cho phép ta duyệt từng phẩn tử của mảng, hay từng thuộc tính của Object. Ta có trong tay pipes là công cụ cho phép ta chuyển hóa về mặt hiển thị của dữ liệu . AGHHHH ... ngFor pipes .... Hãy cùng thử làm một cái pipes cho phép ta duyệt từng phần tử theo cặp key-value. Mục đích cuối cùng là để ta có thể viết theo kiểu như

<table class="table table-striped">
    <tbody>
        <tr class="odd gradeX" *ngFor="let pair of item | keys">
            <td> {{ pair.key }} </td>
            <td> {{ pair.value }} </td>
        </tr>
    </tbody>
</table>

đại loại như thế. Trước tiên, ta cần viết custom pipe của mình. hãy tạm đặt tên nó là KeysPipe. Hãy tạo một file typescript keys.ts, đặt đâu tùy bạn, nhưng nên tổ chức code sao cho hợp lí một chút.

import { Pipe, PipeTransform } from '@angular/core'

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
}

Tạm cái khung thế đã. Ở đây, ta đã khai báo keyword dùng để gọi đến pipe của mình , là

@Pipe({name: 'keys'})

export class KeysPipe này để có thể sử dụng ở chỗ khác . Và tất nhiên, ta viết dựa trên bộ khung mà Angular2 đã dựng sẵn , nên ta cho nó implements PipeTransform . Mở PipeTransform ra, ta sẽ thấy , nó chỉ là cái interface, trong đó khai báo method transform(value: any, ...args: any[]). Cùng implements nó vào trong KeysPipe của ta

import { Pipe, PipeTransform } from '@angular/core'

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push({key: key, value: value[key]});
    }
    return keys;
  }
}

Đơn giản chỉ vậy thôi. Ở đây, ta không cần nhận tham số đầu vào gì , nên không cần động tới biến args . Trước tiên ta khai báo một mảng keys để làm nơi chứa kết quả trả ra . value là biến đầu vào, là giá trị ta cần phải biến đổi, ở đây ta hiểu là một Object. Ta đi duyệt từng thuộc tính của value for (let key in value) { , và push nó vào trong array chứa keys theo từng cặp giá trị key-value.

Việc cuối cùng cần làm trước khi ta có thể sử dụng | keys , ta phải khai báo pipes này trong projcect của mình. Trong file app.module.ts, ta thêm vào

import { KeysPipe } from '../pipes/keys';

var declarations: any[] = [
    // Other Component
    KeysPipe
];

Và lúc này, ta đã có thể sử dụng custom pipe của mình, tương tự như các pipes build-in khác của Angular 2.