Input Output และ ตัวดำเนินการนิพจน์ของ template ใน Angular

เขียนเมื่อ 6 ปีก่อน โดย Ninenik Narkdee
pipe operator safe navigation input output template syntax template reference variable

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ pipe operator safe navigation input output template syntax template reference variable

ดูแล้ว 11,111 ครั้ง


เนื้อหาต่อไปนี้เป็นตอนสุดท้ายของบทความเกี่ยวกับ template syntax เบื้องต้น ซึ่งเรายัง
เหลือหัวข้อที่น่าสนใจหลักๆ อยู่อีก 3 หัวข้อ มาต่อกันที่หัวข้อแรกกันเลย
 
 

ตัวแปรอ้างอิง template - Template reference variables ( #var )

เราได้พูดไปบ้างแล้วเกี่ยวกับตัวแปรอ้างอิง template ตัวแปรอ้างอิง template หรือ template reference variable
จะอ้างอิงถึง DOM element ภายใน template รวมทั้งสามารถใช้อ้างอิงถึง Angular component , directive หรือ 
web component 
 
หมายเหตุ: web component นี้น่าจะเป็นอะไรใหม่ ถ้าสนใจลองเข้าไปดูผ่านลิ้งค์นี้ 
https://developer.mozilla.org/en-US/docs/Web/Web_Components แต่สำหรับของเราแค่รู้ว่ามีก็พอ
 
เรามาดูตัวอย่างการใช้งานตัวแปรอัางอิง template กัน
 


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

@Component({
  selector: 'my-app',
  template: `
  <input #phone placeholder="phone number">
  <button (click)="callPhone(phone.value)">Call</button>
  `,
})
export class AppComponent  { 

  callPhone(phonenumber:string){
    console.log(phonenumber);
  }

}
 
จากตัวอย่าง เรากำหนดตัว "phone" เป็นตัวแปรอ้างอืง template ด้วยการใช้เครื่องหมาย (#) ไว้ด้านหน้า
ชื่อตัวแปรที่เราต้องการ อย่าง #phone ก็คือการกำหนดตัวแปร phone ลงบน <input> element
 
<input #phone placeholder="phone number">
 
โดยเราสามารถใช้งานตัวแปร phone นี้ซึ่งเป็นตัวแปรอ้างอิง template ในส่วนใดก็ได้ใน template นี้ อย่างในโค้ด
template บรรทัดต่อมา ตัวแปร phone ที่ถูกประกาศใน <input> element ถูกนำมาใช้งานใน <button> element
ซึ่งเป็นอีกส่วนของ template
 
<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Call</button>
 
จะเห็นว่าเราส่งค่า "value" property ของตัวแปร phone เข้าไปใน คำสั่ง callPhone()
นั้นแสดงว่าตัวแปร phone ที่ได้จากการอ้างอิงตัวแปร template ก็คือ DOM element 
<input placeholder="phone number"> นั่นเอง 
 
เราลองมาปรับโค้ดนิดหน่อย โดยแทนที่จะส่ง phone.value เข้าไป เราจะส่ง phone ที่เป็น
DOM element object เข้าไปแทน
 


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

@Component({
  selector: 'my-app',
  template: `
  <input #phone placeholder="phone number">
  <button (click)="callPhone(phone)">Call</button>
  `,
})
export class AppComponent  { 

  callPhone(phonenumber:any){
    console.log(phonenumber);
    console.log(typeof phonenumber);
    console.log(phonenumber.value);
    console.dir(phonenumber);   
  }

}
 
ผลลัพธ์ที่ได้ console.log(phonenumber); แรกเราจะได้ค่า
element ที่ตัวแปร phone อ้างอิง
<input placeholder="phone number">
 
console.log(typeof phonenumber); เราได้ทราบว่า phone เป็น Object
object
 
console.log(phonenumber.value); เราสมมติดึงค่า property "value" ของ Object มาแสดง
เราก็จะได้ค่าของข้อมูลที่เรากรอก
0000000000
 
และสุดท้ายเราใช้ console.dir(phonenumber); โดยการใช้ console.dir()  จะทำให้เราทราบว่า
Object นี้มี property อะไรอีกบ้างที่เราสามารถใช้งาน นอกจาก "value" property
 

ดูภาพผลลัพธ์ประกอบ

 

 
 
สำหรับกรณีเราต้องการอ้างอิงตัวแปรจาก <form> โดยทั่วไปแล้วเราจะไม่ใช้วิธีกำหนดตัวแปรอ้างอิง template
ให้กับ input element แต่ละตัว แต่เราจะกำหนดให้กับ <form> element ตัวเดียว จากนั้นใช้การอ้างอิงจากชื่อ
ของ element ในการเรียกใช้งาน สามารถทบทวนการใช้งาน form ได้ทีหัวข้อบทความ
 
"ทบทวนบทความได้ที่"
การส่งค่าข้อมูลจาก Form ใน Angular app เบื้องต้น 
 
 

ข้อสังเกตเกี่ยวกับตัวแปรอ้างอิง template - Template reference variable

ตามที่เราเคยย้ำมาแล้วในบทความตอนที่ผ่านมาว่าตัวแปร template reference variable (#phone) ไม่ใช้ตัวแปรเดียวกัน
กับตัวแปร template input variable (let phone) อย่างกรณีที่เราเคยใช้กำหนดในการใช้งาน *ngFor
 
เราสามารถกำหนดชื่อตัวแปร template reference variable กับ template input variable เหมือนกันได้ เพราะ
เป็นคนละตัว 
ซึ่งการกำหนดตัวแปร template reference variable นั้นเราไม่ควรกำหนดให้มีเชื่อซ้ำกัน เพราะ scope หรือขอบเขต
การเรียกใช้งานตัวแปรนี้ จะครอบคลุมทุกส่วนใน template ดังนั้นถ้าชื่อตัวแปรซ้ำกัน อาจจะเกิดข้อผิดพลาด ไม่สามารถ
อ้างอิงค่าที่ถูกต้องได้
 
นอกจากการกำหนดตัวแปรโดยใช้ (#) แล้ว เรายังสามารถกำหนดตัวแปร template reference variable โดยการ
ใช้คำว่า "ref-" ไว้ด้านหน้าเพื่อกำหนดตัวแปรได้ ยังตัวอย่างเช่น ref-fax แทน #fax  แบบนี้เป็นต้น
 
<input ref-fax placeholder="fax number">
<button (click)="callFax(fax)">Call</button>
 

 

การใช้งาน Input and output properties ( @Input and @Output )

เราได้ทำความรู้จักการใช้งานการเชื่อมข้อมูลจาก component มาใช้งานในนิพจน์ของ template (template
expression) และคำสั่งควบคุม template (template statement) ซึ่งจะอยู่ด้านขวามือของการประกาศการเชื่อม
โยงข้อมูล เราเรียกส่วนนี้แหล่งข้อมูลเชื่อมโยง
 
ในหัวข้อนี้เราจะมาพูดถึงการเชื่อมโยงเป้าหมาย ที่เป็น directive property ที่อยู่ด้านซ้ายมือของการประกาศการเชื่อม
โยงข้อมูล โดย directive property เหล่านี้กำหนดเป็น inputs หรือ outputs
 
ทบทวนความเข้าใจก่อนเข้าสู่รายละเอียด
    - จำไว้เสมอว่า component ทุกตัวก็คือ directive
    - ในการกำหนดการเชื่อมโยงข้อมูลตามที่เราได้ทำความรู้จักมาแล้วนั้น เป็นการเชื่ยมโยงข้อมูล
       ระหว่างข้อมูลที่เป็นเป้าหมาย กับ แหล่งข้อมูล หรือ target กับ source
    - target จะอยู่ด้านซ้ายมือของเครื่องหมาย =  ส่วน source จะอยู่ฝั่งขวามือ
    - target ก็คือ property หรือ event ที่อยู่ในเครื่องหมาย [], () หรือ [()]  ส่วน source ก็คือค่าที่อยู่ใน
       สัญลักษณ์ ("...") หรือ อยู่ในรูปแบบการแทรกค่าตัวแปร ({{}})
    - ทุกๆ ค่าที่เป็น source directive เช่น component property , คำสั่งหรือฟังก์ชั่นใน component สามารถ
       ทำการเชื่อมโยงข้อมูลกับ template ได้ โดยไม่ต้องกำหนดอะไรพิเศษเพิ่มเติมเข้าไปในนิพจน์ของ template
       หรือเข้าไปในคำสั่งควบคุม template 
    - เราไม่สามารถเข้าถึงและใช้งาน target directive ได้ เราสามารถทำได้เพียงเชื่อมโยง property นั้นโดยการ
       ระบุให้ชัดว่าส่วนนั้นเป็น inputs หรือ outputs
 
"ทบทวนเพิ่มเติมได้ที่บทความ"
การเชื่อมโยงข้อมูล Data Binding รูปแบบต่างๆ ใน Angular 
 
ต่อไปเราลองมาดูโค้ดตัวอย่างกัน เพื่อทำความรู้จักกับ Input Output property เบื้องต้นกัน
 


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

@Component({
  selector: 'my-app',
  template: `
  <img [src]="iconUrl"/>
  <button (click)="onSave()">Save</button>
  <hr>
  <p myHighlight (myClick)="clickMessage=$event">
  Highlight me!</p>
  {{clickMessage}}
  <hr>
  <staff [myHighlight]="color" (myClick)="clickMessage2=$event"></staff>
  {{clickMessage2}}
  `,
})
export class AppComponent  { 
  color:string;
  clickMessage:string;
  clickMessage2:string;
  iconUrl:string;

  constructor(){
    this.iconUrl = "mypic.png"
    this.color = "blue";
  }
  onSave(){
    console.log("click");
  }

}
 
 
เรายกเฉพาะส่วนนี้มาดูกัน

<img [src]="iconUrl"/>
<button (click)="onSave()">Save</button>
 
"iconUrl" กับ "onSave" เป็น ค่าขอบเขตข้อมูล หรือ data-bound ใน AppComponent หรือที่เรารู้จักกัน
ในชื่อว่า component property ซึ่งก็คือตัวแปรใน component  โดยค่าเหล่านี้จะถูกอ้างอิงถึงเมื่อมีการเชื่อมโยง
ข้อมูลโดยจะอยู่ทางด้านขวาของเครื่องหมาย (=)
    ทั้งสองค่านี้ไม่ใช่ค่า input หรือ output ของ component  ค่าเหล่านี้เป็น source หรือแหล่งข้อูลสำหรับการ
เชื่อมโยงตามที่เราได้อธิบายไปแล้วด้านบน เกี่ยวกับ source และ target  ส่วน target ของการเชือมโยง
ข้อมูลก็คือ "src" และ "click" ซึ่งเป็น property และ event ของ element เป้าหมาย
 
ต่อไปมาดูโค้ดส่วนนี้ต่อ
 
<p myHighlight (myClick)="clickMessage=$event">Highlight me!</p>
 
เราจะเห็นว่า myHilight กับ myClick นั้น เป็น attribute directive ที่เราสร้างขึ้นมาใช้งานเอง
การเชื่อมโยงข้อมูลของ myHighlight เป็นแบบ property binding ส่วนการเชื่อมโยงข้อมูลของ
myClick เป็นแบบ event binding 
 
ดูต่อที่ตัวอย่างโค้ดต่อไป จะได้เห็นภาพชัดเจนขึ้น
 
<staff [myHighlight]="color" (myClick)="clickMessage2=$event"></staff>
 
    "staff" เป็น component directive  
    "myHighlight" เป็น attribute directive เชื่อมโยงข้อมูลด้วย property ให้มีค่าเท่ากับ color 
จะเห็นว่า "myHighlight" จะรับค่าจากนิพจน์ทางขวามือ หรือก็คือตัวแปร color ที่เป็นค่าจากนิพจน์นี้จะถูกนำ
ไปใช้งานใน "myHighlight" property ใน directive อีกที เราจึงเรียก "myHighlight" นี้ว่า 
ส่วนของ input property
    "myClick" เป็น attribute directive เชื่อมโยงข้อมูลด้วย event ให้ทำคำสั่งกำหนดค่าให้ตัวแปร
clickMessage2 ให้มีค่าเท่ากับ $event object จะเห็นว่า "myClick" event จะส่งค่า $event object ออกมาจาก
directive อีกที เราจึงเรียก "myClick" นี้่ว่า ส่วนของ output property
 
ให้จำแบบนี้ว่า ส่งค่าเข้าไปใน property เรียกว่า input ส่งค่าออกเรียกว่า output
 
การที่เราจะเรียกใช้งาน attribute directive ที่กำหนดเองมาใช้งาน ก็จะคล้ายๆ กับการใช้งาน component
เราต้องไป import และประการใช้งาน directive class นั้นๆ ก่อน อย่างในที้ประกาศไว้ใน @NgModule
ในไฟล์ app.module.ts 
 

ไฟล์ app.module.ts 

 

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

import { AppComponent }  from './app.component';
import { StaffpComponent } from './components/staff.component';

import { HighlightDirective } from './directives/highlight.directive';
import { ClickDirective } from './directives/myclick.directive';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ 
    AppComponent,
    StaffpComponent,
    HighlightDirective,
    ClickDirective 
  ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
 
เราลองเข้าไปดูไฟล์ attribute directive ที่เราสร้างขึ้นมาทั้งสองอันข้างต้นกันดู พร้อมทำความรู้จักกับ
การใช้งาน @Input และ @Output decorator เบื้องต้นกัน
 

ไฟล์ highlight.directive.ts


 

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({ 
    selector: '[myHighlight]' 
})
export class HighlightDirective {
  constructor(private el: ElementRef) { }

  @Input() defaultColor: string;
  @Input('myHighlight') highlightColor: string;

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || this.defaultColor || 'red');
  }
  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }
  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
 
เราจะไม่ลงลึกในรายละเอียดการทำงานทั้งหมด เพราะเนื้อหาเกี่ยวกับการสร้าง directive ขึ้นมาใช้งาน เราจะ
ได้อธิบายเพิ่มเติมในตอนต่อๆ ไป  ในที่นี้เราเข้ามาดูการใช้งาน @Input decorator กัน  
    จากโค้ดเราจะเห็นว่าไฟล์นี้เป็นการสร้าง directive class รูปแบบจะคล้ายๆ กับการสร้าง component
ตัว selector ที่กำหนด ก็คือชื่อ property ที่เรานำไปใช้งาน  การที่เราจะส่งค่าจากการเชื่อมโยงข้อมูลด้วย 
property เข้ามาใช้งานใน directive class นี้เราต้องกำหนดค่า input ตามรูปแบบดังนี้
    สังเกตบรรทัด
 
@Input() defaultColor: string;
@Input('myHighlight') highlightColor: string;
 
การกำหนด input property โดยใช้ @Input decorator ก็เพื่อบอก Angular ให้รู้ว่า ตัวแปรนี้เป็น public สามารถ
เรียกใช้งานจาก component หลักได้ จากตัวอย่างโค้ดบางส่วน

<staff [myHighlight]="color" (myClick)="clickMessage2=$event"></staff>
 
เราสามารถใช้งาน "defaultColor" property ได้ โดยสามารถเพิ่มเข้าไปดังนี้
 
<staff [myHighlight]="color" defaultColor="violet" (myClick)="clickMessage2=$event"></staff>
 
defaultColor เป็น input รับค่าแบบ string กรณีเราต้องใช้นิพจน์ที่ให้ค่าเป็น string เราก็สามารถทำการ
เชื่อมโยงด้วย property เป็น
 
<staff [myHighlight]="color" [defaultColor]="color" (myClick)="clickMessage2=$event"></staff>
 
เมื่อ color คือตัวแปรเก็บชื่อ color ที่เป็น string
 
ส่วน "myHighlight" เรามาดูความแตกต่างของการกำหนด @Input จะเห็นว่า มีการใช้ชื่อเรียกแทน ในการ
อ้างอิงตัวแปร สมมติเรากำหนด เป็นรูปแบบคล้ายๆ defaultColor
 
@Input() myHighlight: string;
 
เราก็สามารถใช้งานได้ คล้ายกับตัวอย่าง
 
<p myHighlight (myClick)="clickMessage=$event">Highlight me!</p>
 
แต่โดยทั่วไป เราต้องการให้การใช้งาน property นี้มีความยึดหยุ่นรองรับการปรับแต่งค่าได้ เช่น 
สมมติเราต้องการรับค่าชื่อสี เข้ามาใช้งานใน directive เราอาจจะกำหนด เป็น
 
@Input() defaultColor: string;
@Input() highlightColor: string;
@Input() myHighlight: string;
 
ถ้าเราใช้แค่สอง input property แค่สองตัว คือ myHighlight กับ highlightColor เวลานำไปใช้เชื่อมต่อ
 
<p myHighlight [highlightColor]="color">test</p>
 
รูปแบบข้างต้นเราต้องการใช้ใช้งาน การ highlight ข้อความของ <p> element พร้อมกับรับค่า สีสำหรับทำ 
highlight ผ่าน highlightColor property ไปพร้อมๆ กันด้วย และถึงแม้จะเป็นวิธีที่สามารถทำได้ แต่จะดีกว่าไหม 
ถ้าเราใช้ property เพียงตัวเดียวเพื่อกำหนดแทนสองค่าได้ หรือให้อยู่ในรูปแบบนี้แทน
 
<p [myHighlight]="color">test</p>
 
และนี้คือเหตุผลที่ @Input decorator รองรับการใช้ชื่อเรียกแทนได้ ในการกำหนดค่า โดยจากเดิมเรากำหนด 3 ค่า
ก็เปลี่ยนเป็น สองค่า โดยรวมค่าที่ใช้ร่วมกันมาเป็นค่าเดียว ดังนี้
 
@Input() defaultColor: string;
@Input('myHighlight') highlightColor: string;
 
ลักษณะด้านบน คือใช้ myHighlight แทนทั้ง myHighlight และ highlightColor พร้อมกัน
 
 
ต่อไปเรามาดูในส่วนของไฟล์ myclick.directive.ts สำหรับการใช้งาน @Output decorator กันต่อ
 

ไฟล์ myclick.directive.ts



 
 
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';

@Directive({
    selector: '[myClick]'
})
export class ClickDirective {
  @Output('myClick') clicks = new EventEmitter<string>(); // 

  toggle = false;

  constructor(el: ElementRef) {
    el.nativeElement
      .addEventListener('click', (event: Event) => {
        this.toggle = !this.toggle;
        this.clicks.emit(this.toggle ? 'Click!' : '');
      });
  }
}
 
จากโค้ดเราจะเห็นว่ามีการสร้าง directive class ชื่อ ClickDirective กำหนดให้ selector เท่ากับ [myClick]
โดยตัว selector ที่กำหนดก็จะถูกนำไปใช้เป็น property ในการเชื่อมโยงข้อมูลด้วย event
การที่เราจะส่งค่า จาก directive class ไปใช้งานได้นั้น เราต้องกำหนดค่า output ตามรูปแบบนี้
 
@Output('myClick') clicks = new EventEmitter<string>();
 
สังเกตว่ามีการใช้ชื่อเรียกแทน คล้ายกับรูปแบบการกำหนดใน @Input decorator 
ในที่นี้จะไม่ขอลงลึกถึงรายละเอียดการทำงาน ให้เราเข้าใจแค่รูปแบบการกำหนด @Output decorator 
เท่านั้นพอ
 
 
เราสามารถกำหนด input property และ output property นอกจากรูปแบบที่กำหนดใน class ด้วย
@Input decorator และ @Output decorator แล้วยังสามารถกำหนดใน metadata ใน @Directive 
ดังนี้แทนได้
 

กำหนดใน @Directive metadata กรณี input property

 

 
 
@Directive({ 
    selector: '[myHighlight]',
    inputs:['defaultColor','highlightColor:myHighlight'] 
})
 

กำหนดใน @Directive metadata กรณี output property

 
 

@Directive({
    selector: '[myClick]',
    outputs:['clicks:myClick']
})
 

 
 

ตัวดำเนินการของนิพจน์ template - Template expression operators

การกำหนดนิพจน์ของ template หรือ template expression เป็นส่วนหนึ่งที่ใช้งานในรูปแบบของ JavaScript 
โดยมีการเสริมด้วยตัวดำเนินการพิเศษ  pipe operator ( | ) และ safe navigation operator ( ?. )
 

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

ในบางกรณีเราต้องการให้ผลลัพธ์ของนิพจน์ หรือ expression นั้นมีการแปลงค่าหรือรูปแบบข้อมูลก่อนนำไปใช้งาน
หรือก่อนนำมาแสดง ยกตัวอย่างเช่น ต้องการแสดงตำเลขในรูปแบบจำนวนเงินตรา หรือการปรับให้ข้อความแสดง
เป็นตัวพิมพ์ใหญ่ หรือการกรองลิสต์รายการและจัดเรียงรายการใหม่ เหล่านี้เป็นต้น
 
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'
    };
  }

}
 
การแสดงหัวข้อเป็นตัวพิมพ์ใหญ่ ทั้งหมด ตามโค้ดด้านล่าง
 
<div>{{title | uppercase}}</div>
 
ข้อความ "Title through uppercase pipe" จะแสดงเป็นตัวพิมพ์ใหญ่ทั้งหมด โดยจะกลายเป็น
"TITLE THROUGH UPPERCASE PIPE"  
 
หรือเราสามารถใช้งาน pipe ฟังก์ชั่นอย่างต่อเนื่องได้โดยสามารถกำหนดได้ดังนี้
 
<div>{{title | uppercase | lowercase}}</div>
 
หรือสามารถกำหนด parameter ให้กับ pipe ตามรูปแบบ
 
<div>My's birthday is {{ birthday | date:"MM/dd/yy" }} </div>
 
หรือใช้ json pipe ในการ debug ตัวค่า property ต่างๆ ใน object
 
<div>{{myObject | json}}</div>
 

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

 
 
รายละเอียดเกี่ยวกับ pipe เพิ่มเติมเราคงะได้พบเจอและทำความเข้าใจเพิ่มเติมอีกต่อไป



 

การใช้งาน safe navigation operator ( ?. ) และ null ใน path ของ property 

ตัวดำเนินการ safe navigation ( ?. ) เป็นวิธีที่คล่องและสะดวกในการใช้ป้องกันค่าที่เป็น null หรือ undefined 
ใน path ของ property
 
ดูตัวอย่างโค้ดต่อไปนี้
 
 

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

@Component({
  selector: 'my-app',
  template: `
  <div>The title is {{title}}</div>
  <div>myObject age property is {{myObject.age}}</div>
  <div *ngIf="myObject">myObject age property is {{myObject.age}}</div>
  <div>myObject age property is {{myObject && myObject.age}}</div>
  <div>myObject age property is {{myObject?.age}}</div>
  `,
})
export class AppComponent  { 

  title:string;
  myObject:{};
  constructor(){
    // this.myObject = {
    //     id:1,
    //     name:'john'
    // };
  }

}
 
เมื่่อเราเรียกใช้งานตัวแปร title ซึ่งไม่ได้กำหนดค่าให้มัน จึงมีค่าเป็น null หรือ undefined
 
<div>The title is {{title}}</div>
 
เวลาแสดงผล ก็จะแสดงเฉพาะข้อความ The title is และไม่มีการแจ้ง error ใดๆ ส่วนนี้
ลักษณะดังกล่าวยังถึอว่าสมเหตุสมผล เพราะอย่างน้อยไม่มีการแจ้ง error หรือโปรแกรมทำงานผิด
พลาดแต่อย่างใด
 
ต่อไปบรรทัด
 
<div>myObject age property is {{myObject.age}}</div>
 
จะเห็นว่า myObject เราไม่ได้กำหนดค่าให้ จึงเป็น null หรือ undefined 
เมื่อเรามีการเรียกใช้ path ของ property ตามรูปแบบข้างต้น 
JavaScript แสดง error ทาง console ว่าเกิดการผิดพลาดในการอ้างอิงตัวแปร undefined
 
TypeError: Cannot read property 'age' of undefined
 
กรณี error ในลักษณะข้างต้นจะไม่เกิดขึ้นเลย ถ้าเรากำหนดให้ myObject ต้องไม่เป็นค่า null หรือ undefined
อย่างไรก็ตาม ในบางครั้งค่า null ก็จำเป็นตามเงื่อนไขของเวลา ยกตัวอย่างเช่น เริ่มต้นไม่มีข้อมูลใดๆ ค่า
ก็ควรจะเป็น null หลังจากนั้นค่าค่อยเปลี่ยนแปลงตามข้อมูลที่เพิ่มหรือดึงเข้ามา
 
ดังนั้นวิธีการที่เป็นไปได้ในการแก้ปัญหา เพื่อที่ให้ให้กรณีค่าตัวแปรใน path ของ property เป็น null หรือ undefined
เราสามารถกำหนดในลักษณะนี้ได้ โดยใช้ *ngIf
 
<div *ngIf="myObject">myObject age property is {{myObject.age}}</div>
 
ถ้าตัวแปร myObject ไม่เท่ากับ null ให้แสดงค่า property path ได้
 
หรือใช้แบบนี้แทน
 
<div>myObject age property is {{myObject && myObject.age}}</div>
 
ให้แสดงค่า property path ได้ ก็ต่อเมื่อ myObject ไม่เท่ากับ null หรือ undefined 
 
วิธีแก้ปัญหาทั้งสองรูปแบบทำให้เวลารัน จะไม่เกิด error ขึ้น 
แต่ปัญหาก็คือ จะทำอย่างไร ถ้า path ของ property มีรูปแบบที่ยาวกว่าเดิม เช่นในรูปแบบ a.b.c.d.
 
ตัวดำเนินการ safe navigation ( ?. ) จึงเป็นวิธีที่คล่องและสะดวกในการใช้ป้องกันค่าที่เป็น null หรือ 
undefined ในการกรณีดังกล่าวนี้ โดยสามารถกำหนดในรุปแบบ
 
<div>myObject age property is {{myObject?.age}}</div>
 
ผลที่ได้กรณีตัวแปร myObject เป็น null หรือ undefined ก็คือ ข้อความแสดงแค่
myObject age property is  และไม่แสดง error หรือไม่ทำให้โปรแกรมหยุดทำงาน
 
การใช้ตัวดำเนินการ safe navigation ( ?. ) ยังสามารถใช้งานในรูปแบบที่มีการเรียก path ของ  property
ที่ยาวกว่าปกติ เช่น {{myObject?.age?.year}} แบบนี้เป็นต้นได้
 
 
สำหรับเนื้อหาเกี่ยวกับ template syntax เบื้องต้น ก็จบลงเพียงเท่านี้ ต่อไปเราจะศึกษาเกี่ยวกับหัวข้ออะไร
รอติดตาม ที่สำคัญจะเห็นว่า เนื้อหาจะค่อยๆ เข้มข้นและยากขึ้นเรื่อยๆ ขอให้ทบทวนบ่อยๆ และ
ทดสอบรันโค้ดตัวอย่างประกอบ จะช่วยให้ทำความเข้าใจได้ง่ายขึ้น

 


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



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









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











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