การใช้งาน pipe operator และการสร้าง pipe directive ใน Angular

เขียนเมื่อ 6 ปีก่อน โดย Ninenik Narkdee
pure pipe pipe operator impure pipe pipe directive

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ pure pipe pipe operator impure pipe pipe directive

ดูแล้ว 20,192 ครั้ง


ในบางครั้งเราต้องการให้ผลลัพธ์ของนิพจน์ หรือ expression นั้นมีการแปลงค่าหรือรูปแบบข้อมูลก่อนนำไปใช้งาน
หรือก่อนนำมาแสดง ยกตัวอย่างเช่น ต้องการแสดงตัวเลขในรูปแบบจำนวนเงินตรา หรือการปรับให้ข้อความแสดง
เป็นตัวพิมพ์ใหญ่ เหล่านี้เป็นต้น

 

การใช้งาน pipe operator ( | ) 

Angular ใช้ pipe operator ( | ) ในการจัดการกับรูปแบบการแสดงข้อมูลข้างต้น Pipe เป็นรูปแบบฟังก์ชั่นง่ายๆ ที่รับ
ค่าเข้ามาแล้วคืนค่าที่มีการแปลงค่าแล้วกลับออกมา โดยทั่วไปจะใช้ใน นิพจน์ของ template เพราะสามารถนำไป
ใช้งานได้อย่างง่ายดาย ยกตัวอย่าง
 


 
 
import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
  <div>{{title | uppercase}}</div>
  <hr/>
  <div>{{title | uppercase | lowercase}}</div>
  <hr/>
  <div>My's birthday is {{ birthday | date:"MM/dd/yy" }} </div>
  <hr/>
  <div>{{myObject | json}}</div>
  `,
})
export class AppComponent  { 
 
  title:string;
  birthday:Date;
  myObject:{};
  constructor(){
    this.title = 'Title through uppercase pipe';
    this.birthday = new Date(2017, 3, 10); 
    this.myObject = {
      id:1,name:'john'
    };
  }
 
}
 

ตัวอย่างผลลัพธ์ที่ได้จากการใช้งาน Pipe operator

 

 
 
จากรูปแบบการใช้งาน Pipe ข้างต้น เราจะเห็นว่า Pipe ใช้ในการนำข้อมูลเข้าไปทำการจัดรูปแบบหรือเปลี่ยนรูปร่าง
หน้าตาใหม่ ตามที่ต้องการ ลองมาลงระเอียดเกี่ยวกับ Pipe ในการจัดการกับวันที่กันดู
 
import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
  <div>My's birthday is {{ birthday | date }} </div>
  `,
})
export class AppComponent  { 
 
  birthday:Date;
  constructor(){
    this.birthday = new Date(2017, 3, 10); 
  }
 
}
 
ดูในส่วนของ component template
 
<div>My's birthday is {{ birthday | date }} </div>
 
ภายในนิพจน์การแทรกค่าตัวแปร เรามีการส่งผ่านตัวแปร birthday ซึ่งเป็นค่า component property เข้าไปใน
ตัวดำเนินการ ( | ) pipe เพื่อนำค่าเข้าไปใน date function ทางขวามือ ในตัวอย่างก็คือ "date" เพื่อแปลงค่าข้อมูล
จากนั้นส่งค่ากลับมาออกมาแสดง
 
 

Built-in pipe มีอะไรบ้าง

pipe ที่มีมาให้ใน Angular ประกอบไปด้วย ดังนี้
 
AsyncPipe ใช้จัดรูปแบบข้อมูลที่ถูกปล่อยออกมาจาก server หรือการทำงานข้อมูลตามช่วงเวลา ไม่พร้อมกัน
 
{{ greeting | async }} // greeting คือตัวแปรค่า ที่จะถูกปล่อยมาจากฝั่ง server หลังทำงานเสร็จสิ้นแล้ว
 
CurrencyPipe ใช้จัดรูปแบบการแสดงเงินตรา
 
{{0.259 | currency:'USD':false}}  // output  USD0.26
{{1.3495 | currency:'USD':true:'4.2-2'}}  // output  $0,001.35
 
DatePipe ใช้จัดรูปแบบการแสดงของวันที่ เช่น
 
{{ dateObj | date }}               // output is 'Apr 10, 2017'
{{ dateObj | date:'medium' }}      // output is 'Apr 10, 2017, 12:00:00 AM'
{{ dateObj | date:'shortTime' }}   // output is '12:00 AM'
{{ dateObj | date:'mm:ss' }}        // output is '00:00'

export class AppComponent  { 
  dateObj = new Date(2017, 3, 10); 

}
 
DecimalPipe  ใช้จัดรูปแบบการแสดงตัวเลข
 
{{ 3.141592 | number }} // output 3.142
 
I18nPluralPipe ใช้จัดรูปแบบการแสดงจำนวนหรือรายการจำนวนต่างๆ ในรูปแบบข้อความแทน ที่สัมพันธ์กัน
 
{{ messages.length | i18nPlural: messageMapping }} // output "One message"

export class AppComponent  { 
  messages: any[] = ['Message 1'];
  messageMapping:
      {[k: string]: string} = {'=0': 'No messages.', '=1': 'One message.', 'other': '# messages.'}; 
}
 
I18nSelectPipe ใช้จัดรูปแบบข้อมูลให้สัมพันธ์ตรงกับค่าข้อมูลที่เลือก
 
{{gender | i18nSelect: inviteMap}} // output "Invite him"

export class AppComponent  { 
  gender: string = 'male';
  inviteMap: any = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
}
 
JsonPipe ใช้จัดข้อมูลให้อยู่ในรูปแบบ JSON string
 
{{myObject | json}} // output  {"id":1,"name":"John"}

export class AppComponent  { 
  myObject = {
      id:1,name:'john'
    };
}
 
LowerCasePipe ใช้จัดรูปแบบข้อมูลให้เป็นตัวพิมพ์เล็ก 
UpperCasePipe ใช้จัดรูปแบบข้อมูลให้เป็นตัวพิมพ์ใหญ่
TitleCasePipe ใช้จัดรูปแบบให้เป็นลักษณะหัวข้อเรื่องโดยตัวอักษรตัวแรกของแต่ละคำเป็นตัวพิมพ์ใหญ่
 
{{title | lowercase }} // output "title through uppercase pipe"
{{title | uppercase }} // output "TITLE THROUGH UPPERCASE PIPE"
{{title | titlecase }} // output "Title Through Uppercase Pipe"

export class AppComponent  { 
  title = 'Title through uppercase pipe';
}
 
PercentPipe ใชัจัดรูปแบบข้อมูลให้อยู่ในรูปแบบเปอร์เซ็นต์
 
{{ 0.259 | percent}} // output 25.9%
 
SlicePipe ใช้จัดรูปแบบของข้อมูลโดยแสดงเฉพาะบางส่วนของข้อมูล
 
{{title | slice:0:4}} // output "Titl"

export class AppComponent  { 
  title = 'Title through uppercase pipe';
}
 
 
ใน Angular จะไม่มี FilterPipe หรือ OrderByPipe ที่ใช้ในการกรองรายการข้อมูลหรือ จัดเรียงข้อมูลเหมือนใน
AngularJS เนื่องจากประสิทธิภาพในการใช้งานที่ช้ามากเมื่อใช้ในการจัดการกับรูปแบบข้อมูลจำนวนมาก
 
 

การกำหนด parameter ใน pipe 

เราสามารถจัดรูปแบบการแสดงข้อมูลให้มีความหลากหลายได้โดยการกำหนด parameter ในการใช้งาน pipe
ทำได้โดยการ ต่อท้ายชื่อของ pipe ด้วยเครื่องหมาย ( : ) แล้วตามด้วยรูปแบบ parameter ที่ต้องการ ยกตัวอย่าง
เช่น ( currency:'EUR' )  ในกรณีที่ pipe นั้นรองรับ parameter ได้หลายค่า ก็จะกำหนดโดยแบ่งแต่ละค่าด้วย (:)
ตัวอย่างเช่น {{title | slice:0:4}}
 

ตัวอย่างการกำหนด parameter ใน DatePipe

{{ dateObj | date }}               // output is 'Apr 10, 2017'
{{ dateObj | date:'medium' }}      // output is 'Apr 10, 2017, 12:00:00 AM'
{{ dateObj | date:'shortTime' }}   // output is '12:00 AM'
{{ dateObj | date:'mm:ss' }}        // output is '00:00'

export class AppComponent  { 
  dateObj = new Date(2017, 3, 10); 

}
 
 

การเรียกใช้งาน pipe แบบต่อเนื่อง

เราสามารถใช้งาน pipe ฟังก์ชั่นอย่างต่อเนื่องได้โดยสามารถกำหนดได้ดังนี้ สมมติเช่น
เราต้องการให้วันเกิดแสดงเป็นตัวพิมพ์ใหญ่ทั้งหมด 
 
{{ dateObj | date | uppercase }} // output is 'APR 10, 2017'

export class AppComponent  { 
  dateObj = new Date(2017, 3, 10); 
}
 
 
 

การสร้าง Pipe Directive มาใช้งาน

เนื้อหาในหัวข้อต่อไปนี้ เราจะลองมาสร้าง pipe directive เพื่อใช้งานใน Angular App ของเรากันดู
โดยจะใช้ตัวอย่างการสร้าง pipe ในการจัดรูปแบบวันที่เป็นภาษาไทยอย่างง่าย
 
ให้เราสร้างไฟล์ชื่อ thaidate.pipe.ts ในโฟลเดอร์ directives โดยกำหนดรูปแบบไฟล์ ดังนี้
 

ไฟล์ thaidate.pipe.ts (แบบเต็ม)

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

@Pipe({
    name: 'thaidate'
})
export class ThaiDatePipe implements PipeTransform {
  transform(date: string, format: string): string {
    let ThaiDay = ['อาทิตย์','จันทร์','อังคาร','พุธ','พฤหัสบดี','ศุกร์','เสาร์']
	let shortThaiMonth = [
        'ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.',
        'ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.'
        ];	
    let longThaiMonth = [
        'มกราคม','กุมภาพันธ์','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน',
        'กรกฎาคม','สิงหาคม','กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'
        ];   
    let inputDate=new Date(date);
    let dataDate = [
        inputDate.getDay(),inputDate.getDate(),inputDate.getMonth(),inputDate.getFullYear()
        ];
    let outputDateFull = [
        'วัน '+ThaiDay[dataDate[0]],
        'ที่ '+dataDate[1],
        'เดือน '+longThaiMonth[dataDate[2]],
        'พ.ศ. '+(dataDate[3]+543)
    ];
    let outputDateShort = [
        dataDate[1],
        shortThaiMonth[dataDate[2]],
        dataDate[3]+543
    ];
    let outputDateMedium = [
        dataDate[1],
        longThaiMonth[dataDate[2]],
        dataDate[3]+543
    ];    
    let returnDate:string;
    returnDate = outputDateMedium.join(" ");
    if(format=='full'){
        returnDate = outputDateFull.join(" ");
    }    
    if(format=='medium'){
        returnDate = outputDateMedium.join(" ");
    }      
    if(format=='short'){
        returnDate = outputDateShort.join(" ");
    }    
    return returnDate;
  }
}
 
เราจะลำดับทำความเข้าไปใจไปทีละส่วนกัน 
ในการสร้าง pipe directive เราจำเป็นต้อง import class ชื่อ "Pipe" และ "PipeTransform"
 
import { Pipe, PipeTransform } from '@angular/core';
 
ต่อมาส่วนของ @Pipe หรือฟังก์ชั่น pipe decorator เรากำหนดชื่อ pipe ที่เราจะใช้งาน ในที่นี้เรากำหนด
เป็นชื่อว่า "thaidate"
 
@Pipe({
    name: 'thaidate'
})
 
ต่อไปมาที่ส่วนของการจัดการรูปแบบ pipe ใน pipe class 
 
export class ThaiDatePipe implements PipeTransform {
  transform(date: string, format: string): string {
 
  }
}
 
เราสร้าง pipe class จากการใช้งานคำสั่งแปลงค่าที่ชื่อว่า "transform" ของ PipeTransform interface 
ตอนสร้าง pipe class เราจึงทำการ implements class "PipeTransform" หรือ ในความหมายก็คือ เราสร้าง
pipe class โดยมี property และ method ให้เหมือนกับ class "PipeTransform" นั่นเอง
    โดยในคำสั่ง "transform" ที่เราเรียกใช้ ใน parameter แรกจะกำหนดตัวแปร ของข้อมูลที่รับเข้ามา
ส่วน parameter ตัวที่สองจะกำหนดเป็นตัวเลือก parameter ของ pipe เพิ่มเติมถ้ามี 
 
ตัวอย่างรูปแบบการใช้งาน
 
  {{ dateObj | thaidate:'full' }} <br>

export class AppComponent  { 
  dateObj = new Date(2017, 3, 10); 
}
 
ถ้าเราอยากรู้ว่า เมื่อมีการใช้งาน pipe ค่าที่ส่งเข้ามาเป็นค่าอะไร เราสามารถทดสอบได้โดยใช้คำสั่ง 
"transform" ได้ดังนี้
 
export class ThaiDatePipe implements PipeTransform {
  transform(a: string, b: string){
      console.log(a);
      console.log(b);
  }
}
 
 
จากตัวอย่างเราจะได้ค่า a ในตัวอย่างเท่ากับ
 
Mon Apr 10 2017 00:00:00 GMT+0700 (SE Asia Standard Time)
 
ซึ่งเป็นข้อมูลรูปแบบของวันที่ 
ส่วนค่า b เราจะได้เป็นค่า parameter ของ pipe ที่เรากำหนด ถ้ามี จะได้ค่าเป็น "full" 
 
เราจัดรูปแบบคำสั่ง "transform" ใหม่ ให้สอดคล้องกับตัวอย่าง 
 
export class ThaiDatePipe implements PipeTransform {
  transform(date: string, format: string): string {
      console.log(date);
      console.log(format);
      return;
  }
}
 
และสุดท้ายก็ส่วนของโค้ดที่เราใช้จัดรูปแบบข้อมูล เริ่มต้นเรากำหนดตัวแปร array เกี่ยวกับวันที่
ในภาษาไทย ที่จำเป็นต้องใช้งาน
 
    let ThaiDay = ['อาทิตย์','จันทร์','อังคาร','พุธ','พฤหัสบดี','ศุกร์','เสาร์']
	let shortThaiMonth = [
        'ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.',
        'ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.'
        ];	
    let longThaiMonth = [
        'มกราคม','กุมภาพันธ์','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน',
        'กรกฎาคม','สิงหาคม','กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'
        ];   
 
ประกอบไปด้วย วันในภาษาไทย เดือนแบบย่อ และแบบเต็ม
 
จากนั้นเราสร้าง Date Object ใหม่ชื่อ inputDate โดยรับค่าวันที่จาก date ที่ถูกส่งเข้ามา
 
let inputDate=new Date(date);
 
ที่นี้เราก็สามารถใช้งานค่าต่างๆ ของ inputDate ที่เป็น Date Object ได้แล้วไม่ว่าจะเป็นหาวันที่
หาค่าเวลา หาปี หาเดือน เหล่านี้เป็นตัน 
เราใช้วิธีเตรียมข้อมูลต่างๆ ที่จะใช้งานไว้ในตัวแปร ชื่อ dataDate
 
 
    let dataDate = [
        inputDate.getDay(),inputDate.getDate(),inputDate.getMonth(),inputDate.getFullYear()
        ];
 
ประกอบไปด้วย วัน,วันที่,เดือน,ปี ข้อมูลทั้งหมดอยู่ในรูปแบบตัวเลข
เช่น 2,10,4,2017
ค่าตัวเลขวัน เราจะใช้อ้างอิง วันในภาษาไทยใน array ชื่อ ThaiDay
ค่าตัวเลขเดือน เราจะใช้อ้างอิง เดือนในภาษาไทยใน array ชื่อ shortThaiMonth และ longThaiMonth
 
ต่อมาเราสร้างตัวแปร array เก็บชุดข้อมูล ouput ในรูปแบบต่าง
 
    let outputDateFull = [
        'วัน '+ThaiDay[dataDate[0]],
        'ที่ '+dataDate[1],
        'เดือน '+longThaiMonth[dataDate[2]],
        'พ.ศ. '+(dataDate[3]+543)
    ];
    let outputDateShort = [
        dataDate[1],
        shortThaiMonth[dataDate[2]],
        dataDate[3]+543
    ];
    let outputDateMedium = [
        dataDate[1],
        longThaiMonth[dataDate[2]],
        dataDate[3]+543
    ];
 
ประกอบไปด้วย outputDateFull , outputDateShort และ outputDateMedium โดยค่าของ array เราก็ใช้
ค่าจากตัวแปร array dataDate 
 
พอได้ส่วนของข้อมูลครบแล้ว ก็มาในส่วนของการจัดการรูปแบบตามเงื่อนไข parameter ของ pipe ที่ส่งมา
ถ้ามี ในที่นี้เราใช้ตัวแปรชื่อ format
 
    let returnDate:string;
    returnDate = outputDateMedium.join(" ");
    if(format=='full'){
        returnDate = outputDateFull.join(" ");
    }    
    if(format=='medium'){
        returnDate = outputDateMedium.join(" ");
    }      
    if(format=='short'){
        returnDate = outputDateShort.join(" ");
    }    
    return returnDate;
 
วิธีการแสดงรูปแบบวันที่ของเราก็คือ ใช้ตัวแปร array เรียกใข้คำสั่ง join(" "); หรือเชื่อมแต่ละตัว
ด้วยช่องว่าง เราก็จะได้ข้อมูลวันที่เป็นรูปแบบ string ซึ่งเงื่อนไขการเลือก array ที่จะใช้ในการ
สร้าง string ด้วยคำสั่ง join(" ") เราก็ใช้ตัวแปร format ซึ่งเป็น parameter ของ pipe ทีส่งเข้ามานั่นเอง
 
ทั้งหมดนี้คือรูปแบบการสร้าง pipe เกี่ยวกับจัดรูปแบบวันที่เป็นภาษาไทย สังเกตว่าใน pipe class เราใช้
let ซึ่งเป็นคำที่ใช้กำหนดตัวแปร block scope คือ ชื่อตัวแปรนี้จะอยู่ภายใน block นี้เท่านั่น block ในที่นี้
ก็คือในคำสั่ง transform() ไม่มีผลกับตัวแปรอื่นที่ชื่อเหมือนกันนอก block นี้ และใน angular ก็ค่อน
ข้างจะแนะนำให้ใช้งาน let สำหรับการใช้งานการกำหนดตัวแปร
 
 

การใช้งาน Pipe ที่กำหนดขึ้นมาเอง

หลังจากที่เราได้สร้าง pipe direactive ขึ้นมาแล้วต่อไป ก็คือส่วนของการใช้งาน และแน่นอนว่า เราต้อง
ทำการ import และประกาศใช้ในไฟล์ app.module.ts เช่นเดียวกับการใช้งาน @Component 
 

ไฟล์ app.module.ts

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

import { AppComponent }  from './app.component';
import { StaffDetailComponent }  from './components/staff-detail.component';
import { StafListComponent } from './components/staff-list.component';

import { ThaiDatePipe } from './directives/thaidate.pipe';

@NgModule({
  imports:      [ 
    BrowserModule,
    ReactiveFormsModule
  ],
  declarations: [ 
    AppComponent,
    ThaiDatePipe,
    StaffDetailComponent,
    StafListComponent
  ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
 
ให้สั่งเกตเฉพาะการกำหนดส่วนของการ import และการประกาศใช้งานใน declarations
ข้างต้นเรา import ThaiDatePipe และประกาศใช้งาน
 
ทดสอบเรียกใช้งานดังนี้
 

ไฟล์ app.component.ts


 
 
import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
  {{ dateObj | thaidate }} <br>
  {{ dateObj | thaidate:'full' }} <br>
  {{ dateObj | thaidate:'medium' }} <br>
  {{ dateObj | thaidate:'short' }} <br>
  `,
})
export class AppComponent  { 
  dateObj = new Date(2017, 3, 10); 
}
 
ผลลัพธ์ที่ได้ ตามรูปแบบที่เรากำหนดเรียกใช้งานแบบไม่มี parameter และ แบบมี parameter
 
 

 
 
เนื่องจากรูปแบบที่ไม่ได้กำหนด parameter เรากำหนดให้ทำงานเหมือนกับการกำหนด parameter เท่ากับ
"medium" ผลที่ได้จึงเหมือนกัน
 
 
ต่อไปเรามาลองสร้าง pipe สำหรับกรองข้อมูลกันดู เหมาะสำหรับข้อมูลประเภท Array Object สมมติเรา
กรอกรายชื่อ user ที่มีอายุ ตามที่เรากำหนด 
 
สร้าไฟล์ชื่อ userage.pipe.ts ในโฟลเดอร์ directives
 

ไฟล์ userage.pipe.ts

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

@Pipe({
    name: 'userage'
})
export class UserAgePipe implements PipeTransform {
  transform(user:any[], min:number,max:number): any[] {
    return user.filter(user => user.age>=min && user.age<=max);
  }
}
 
pipe ของเราทำหน้าที่รับ Array Object ของ user เข้ามา มี parameter สองตัวคือ min และ max เป้น number
คือค่าอายุน้อยสุด กับมากสุด เมื่อรับค่าเข้ามาแล้ว ก็เอาเข้าฟังก์ชั่น เลือกเฉพาะ user ที่มี อายุตามเงื่อนไข
คือ มีอายุมากกว่าหรือเท่ากับ min และ น้อยกว่าหรือเท่ากับ max
 
รูปแบบการใช้งานคร่าวๆ ก็ประมาณ
 
    <li *ngFor="let user of (users | userage:20:30)">
    Name:{{ user.name }} Age: {{user.age}}<br>
    </li>
 
users | userage:20:30 ส่วนนี้คือตัวอย่างเราเรียกใช้ pipe ชื่อ userage ส่งค่า Array Object ของ users เข้าไป
พร้อมกับ parameter สองค่า คือ min = 20 และ max = 30 จากนั้นทำการกรองรายการ users ให้เหลือเฉพาะราย
การตามเงื่อนไขที่ pipe เราทำงาน 
 
มาลองดูตัวอย่างไฟล์ app.component.ts จำลองการใช้งาน
 

ไฟล์ app.component.ts

import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
  <br>
  <ul>
    <li *ngFor="let user of (users | userage:20:30)">
    Name:{{ user.name }} Age: {{user.age}}<br>
    </li>
  </ul>
  <button (click)="addUser()">Add User</button>
  `,
})
export class AppComponent  { 
  users:user[]=[];

  constructor(){
    this.users.push(
      {name:'A',age:23},
      {name:'B',age:30},
      {name:'C',age:34},
      {name:'D',age:25},
      {name:'E',age:20}
    );
  }

  addUser(){
    this.users.push(
      {name:'F',age:25}
    );
  }

}
interface user{
  name:string,
  age:number
}
 
ดูผลลัพธ์ที่ได้ จะเป็นดังนี้
 
 

 
 
จะเห็นว่ารายการ user จะแสดงเฉพาะคนที่มีอายุ มากกว่าหรือเท่ากับ 20 และน้อยกว่าหรือเท่ากับ 30
เป็นไปตามรูปแบบ pipe ที่เราต้องการ
 
 

Pipe กับการตรวจจับค่าที่เปลี่ยนแปลง

Angular เฝ้าดูการเปลี่ยนแปลงที่เกิดขึ้นกับข้อมูลทุกๆ ครั้งที่มีการเปลี่ยนแปลงค่าที่เกิดขึ้นหลังจากเกิด DOM
event ต่างๆ ไม่ว่าจะเป็นการเคาะแป้น การเลื่อนเมาส์ การกำหนดการตั้งเวลา หรือแม้แต่การส่งค่ากลับมา
จากฝั่ง server ซึงค่อนข้างใช้ทรัพยากรในการดำเนินการสูงมาก Angular จึงพยายามจะเลือกวิธี
หรือทำทุกวิถีทางพี่ลดการสูญเปล่าที่เกิดขึ้นนั้นให้น้อยลงและมีความเหมาะสมที่สุด
    ด้วยเหตุผลข้างต้น Angular จึงเลือกใช้ขั้นตอนการตรวจจับการเปลี่ยนแปลงข้อมูลที่ง่าย และทำงาน
รวดเร็วเมื่อเรามีการใช้งาน pipe 
    เรามาดูตัวอย่างเปรียบเทียบการใช้ pipe และไม่ใช่ pipe ในข้อมูลที่มีการเปลี่ยนแปลง
 

การเปลี่ยนแปลงค่าข้อมูลที่ไม่มีการใช้งาน pipe

จากรูปแบบการใช้งาน userage pipe ด้านบน สมมติเราลองตัดออกและไม่ใช้งาน pipe แล้วเรากดปุ่ม
เพื่อเพิ่ม user เข้าไป 
 

ไฟล์ app.component.ts ไม่ได้ใช้งาน pipe

import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
  <br>
  <ul>
    <li *ngFor="let user of users">
    Name:{{ user.name }} Age: {{user.age}}<br>
    </li>
  </ul>
  <button (click)="addUser()">Add User</button>
  `,
})
export class AppComponent  { 
  users:user[]=[];

  constructor(){
    this.users.push(
      {name:'A',age:23},
      {name:'B',age:30},
      {name:'C',age:34},
      {name:'D',age:25},
      {name:'E',age:20}
    );
  }

  addUser(){
    this.users.push(
      {name:'F',age:25}
    );
  }

}
interface user{
  name:string,
  age:number
}
 
เมื่อรันตอนเริ่มต้น เราก็จะแสดงรายการ user จาก Array Object ที่ชื่อ users ดังรูป
 


 
 
หลังจากนั้นเราลองกดปุ่ม Add User เพื่อ user เข้าไปใน Array ผลที่ได้ก็คือ มีรายการแสดงเพิ่มเข้ามา
 


 
 
เราจะเห็นว่า เป็นรูปแบบการทำงานปกติที่เราคุ้นชินมาแล้ว ทีนี้เรามาดูเมื่อเราใช้งาน pipe "userage" กันดูบ้าง
 
 
การเปลี่ยนแปลงค่าข้อมูลที่มีการใช้งาน pipe 
 

ไฟล์ app.component.ts มีการใช้งาน pipe

import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
  <br>
  <ul>
    <li *ngFor="let user of (users | userage:20:30)">
    Name:{{ user.name }} Age: {{user.age}}<br>
    </li>
  </ul>
  <button (click)="addUser()">Add User</button>
  `,
})
export class AppComponent  { 
  users:user[]=[];

  constructor(){
    this.users.push(
      {name:'A',age:23},
      {name:'B',age:30},
      {name:'C',age:34},
      {name:'D',age:25},
      {name:'E',age:20}
    );
  }

  addUser(){
    this.users.push(
      {name:'F',age:25}
    );
    console.log(this.users);
  }

}
interface user{
  name:string,
  age:number
}
 
เมื่อรันมาครั้งแรก เราก็จะเห็นรายการข้อมูลเฉพาะที่เข้าเงื่อนไขของ pipe "userage" คือ user ที่มีอายุ 20-30 ปี
 


 
 
ที่เราทดสอบทำการเปลี่ยนแปลงข้อมูลโดยกดปุ่ม Add user เพื่อเพิ่ม user เข้าไป
 


 
 
สังเกตว่าไม่มีการเปลี่ยนใดๆ เกิดขึ้นในหน้าแสดงผล ทั้งที่ user ที่เราเพิ่มเข้าไป มีอายุ 25 ปี ซึ่งก็เข้า
เงื่อนไขการทำงานของ pipe ก็ตาม แต่ตรง console เราจะเห็นว่าตัวแปร Array มีการเพิ่ม
user "F" เข้าไปจริง แต่ Angular เลือกที่จะไม่ใช้งาน pipe เมื่อค่าข้อมูลเปลี่ยนแปลง ซึ่งสำคัญมาก เพราะ
ถ้าข้อมูลมีจำนวนมากๆ การทำงานในส่วนของ pipe ก็จะเพิ่มขึ้นทวีคูณ และสิ้นเปลืองทรัพยากรในการจัดการ
กับส่วนนี้ไปโดยใช่เหตู 
    วิธีการที่จะทำให้ pipe ทำงาน คือการแทนที่ Array ด้วย Array ใหม่ ซึ่งเดิม pipe จะไม่ทำงาน ถ้า Array 
มีการเปลี่ยนแปลงค่าเกิดขึ้นใน Array เดิม แต่จะทำงานกับ Array ใหม่ เราจึงเปลี่ยนมาเป็นการแทนที่ Array ใหม่ ดังนี้
โดยเปลี่ยนตรงคำสั่ง addUser() จากเดิมเราใช้การ push ค่าเข้าไปใน Array เดิม เปลี่ยนมาใช้การสร้าง Array
ใหม่จากฟังก์ชั่น concat() ทำให้ this.users กลายเป็น Array ใหม่ที่มีค่าจาก เอา Array เดิมมาเพิ่มค่าใหม่เข้าไป
 
  addUser(){
    this.users = this.users.concat({name:'F',age:25});
  }
 
ทดสอบกดปุ่ม Add user อีกครั้ง
 


 
 
เราก็จะได้ user "F" ที่เข้าเงื่อนไข pipe แสดงขึ้นมาดังรูป
 
ถึงแม้การแทนที่ Array จะเป็นวิธีที่มีประสิทธิภาพ ในการใช้งาน pipe ให้แสดงผลค่าข้อมูลที่อัพเดทก็ตาม แต่ใน
ความเป็นจริง และบ่อยครั้ง เราไม่อาจรู้แล้วว่าเมื่อไหร่ข้อมูลจะมีการเปลี่ยนแปลง ไม่ใช่ข้อมูลจะเปลี่ยนแปลง
เมื่อคลิกตามตัวอย่างด้านบนเสมอไป pipe จึงมีรูปแบบการกำหนดประเภทของ pipe เข้ามาช่วย นั่นคือ impure pipe
 
 

รู้จัก Pure and impure pipes

pipe แบ่งออกได้เป็นสองประเภทคือ "pure" และ "impure"  โดย pipe ที่เป็น "pure" นั่นจะเป็นค่าเริ่มต้นของ pipe
ที่เราสร้างขึ้นมา หมายถึง พอเราสร้าง pipe ขึ้นมาตามตัวอย่างด้านบน pipe ของเราก็เป็นประเภท "pure" นั่นเอง
ส่วนวิธีการกำหนดให้ pipe เป็น "impure" คือแค่เพียงเราเพิ่ม ในส่วนของ metadata ใน @Pipe decorator 
โดยกำหนด pure ให้มีค่าเป็น false ดังนี้
 
@Pipe({
    name: 'userage',
    pure:false
})
 
หรือถ้าต้องการอยากให้สามารถใช้งาน pipe ได้ทั้งสองรูปแบบทั้ง pure และ impure โดยไม่ต้องแก้ไขอะไรมาก
วิธีการ ก็คือให้เราสร้าง pipe class ขึ้นมาในไฟล์เดิม และทำการสืบทอดจาก pipe class ที่เป็น pure 
อย่างตัวอย่างของเรา ก็สามารถทำได้ดังนี้
 

ไฟล์ userage.pipe.ts

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

@Pipe({
    name: 'userage'
})
export class UserAgePipe implements PipeTransform {
  transform(user:any[], min:number,max:number): any[] {
    return user.filter(user => user.age>=min && user.age<=max);
  }
}

@Pipe({
    name: 'userageImpure',
    pure:false
})
export class UserAgeImpurePipe extends UserAgePipe {

}
 
เราก็จะได้ pipe ที่ชื่อ userage สำหรับ "pure" pipe และ
ได้ pipe ที่ขื่อ userageImpure สำหรับ "impure" pipe ไว้ใช้งาน
 
เราจะลองนำไปใช้กันดู แต่อย่าลืมไป import class และประกาศใช้งาน UserAgeImpurePipe ใน app.module.ts 
ก่อนเรียกใช้งาน
 
ทดสอบเรียกใช้งานในไฟล์ app.component.ts โดยเราเปลี่ยนชื่อ pipe เป็น "userageImpure" ซึ่งเป็น
impure pipe ซึ่งเมื่อเราใช้งาน และมีการเปลี่ยนแปลงค่าของข้อมูล pipe ก็จะยังทำงาน โดยที่เรา
ไม่ต้องทำการแทนด้วย Array ใหม่ เหมือนวิธีแก้ปัญหาข้างต้นที่กล่าวมา
 

ไฟล์ app.component.ts 

import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
  <br>
  <ul>
    <li *ngFor="let user of (users | userageImpure:20:30)">
    Name:{{ user.name }} Age: {{user.age}}<br>
    </li>
  </ul>
  <button (click)="addUser()">Add User</button>
  `,
})
export class AppComponent  { 
  users:user[]=[];

  constructor(){
    this.users.push(
      {name:'A',age:23},
      {name:'B',age:30},
      {name:'C',age:34},
      {name:'D',age:25},
      {name:'E',age:20}
    );
  }

  addUser(){
    this.users.push(
      {name:'F',age:25}
    );
  }

}
interface user{
  name:string,
  age:number
}
 
จะเห็นว่าเราสามารถใช้คำสั่ง push() เพื่อเพิ่มค่า Array ของข้อมูล โดยไม่จำเป็นต้องแทนที่ด้วย Array ใหม่
 
 

การทำงานของ Pure pipe

จากรูปแบบการทำงานข้างต้น เราจะเห็นการทำงานของ pipe ที่เป็น pure pipe นั่นจะทำงานกับข้อมูล
ค่าเริ่มต้น อย่างข้อมูล user เริ่มต้นเมื่อรัน App ก็มีการใช้งาน pure pipe ทำการแสดงเฉพาะรายชื่อ user
ที่เข้าเงื่อนไข ซึ่งการทำงานกับข้อมูลเริ่มตั้นนั้นอาจจะเป็นข้อมูลประเภท (String, Number, Boolean, Symbol) ก็ได้
นอกจากนั้น pure pipe ยังทำงานกับ Object ที่มีการเปลี่ยนค่าอ้างอิงใหม่ในลักษณะที่มีการสร้าง Object ขึ้นมาใหม่
มาแทนที่ตัวเดิม ในข้อมูลประเภท (Date, Array, Function, Object) ได้อีกด้วย
 
Angular จะไม่สนใจการเปลี่ยนแปลงองค์ประกอบภายในของ Object โดยจะไม่เรียกใช้งาน pure pipe เช่น ถ้า
เราทำการแก้ไขข้อมูลเดิม หรือการเพิ่มค่าใหม่เข้าไปใน Array หรือการอัพเดท property ของ Object 
ลักษณะเหล่านี้ pure pipe จะไม่ทำงาน
 
ดูเหมือนจะเป็นข้อจำกัด แต่จริงๆ แล้ว Angular ทำงานในส่วนนี้เร็วมาก อย่างการใช้การแทนที่ด้วยตัวอ้างอิง
Object ใหม่ ทำงานได้เร็วกว่าการเปรียบเทียบกับค่าเดิมเสียด้วยซ้ำ ดังนั้น Angular จึงสามารถที่จะตัดสินใจ
ได้อย่างรวดเร็วถ้าข้ามการทำงานของ pipe และไม่อัพเดทหน้าแสดงผล
 

การทำงานของ Impure pipes

Angular ทำคำสั่ง "impure" pipe ในระหว่างการเปลี่ยนแปลงที่เกิดขึ้นกับในทุกๆ component  ซึ่ง "impure" pipe
จะถูกเรียกใช้งานบ่อยมาก บ่อยเท่าๆ กับทุกๆ ครั้งที่มีการเคาะที่แป้นพิมพ์หรือการเลื่อนเมาส์
 
ด้วยเหตุนี้การใช้งาน impure pipe เราจึงจะต้องให้ความสนใจเป็นพิเศษ เพราะค่อนข้างจะสิ้นเปลืองในการใช้งาน
ทรัพยากรของระบบ และยิ่งถ้าใช้งาน impure pipe ไปนานๆ ยิ่งจะทำให้การใช้งานช้าลง มีผลกับประสบการณ์ในการ
ใช้งานที่ไม่น่าพอใจขึ้นกับผู้ใช้งานได้
 
impure pipe ที่น่าจะสนใจอีกตัวที่เป็น Build-in pipe คือ AsyncPipe ถ้ามีโอกาสเราจะได้ลงลึกถึงรายละเอียด
เพิ่มเติมต่อไป  

 
 
สำหรับเนื้อหาเกี่ยวกับ pipe ก็ขอจบเพียงเท่านั้น เราอาจจะได้เห็นภาพ และรูปแบบการใช้งาน pipe directive เพิ่มขึ้น
รวมทั้งรู้แนวทางการในการสร้าง pipe ขึ้นมาใช้งานเองได้
ส่วนเนื้อหาต่อไปจะเกี่ยวกับอะไร รอติดตาม


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ











URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ