Babel Coder

[Angular2#1] รู้จัก Angular 2 โครงสร้างและคอนเซ็ปต์ของแอพพลิเคชันใน Angular 2

intermediate

 บทความนี้เป็นส่วนหนึ่งของชุดบทความ [ชุดบทความ] สอนสร้างและใช้งานเว็บแอพพลิเคชันด้วย Angular2

แม้ขณะที่เขียนบทความนี้ Angular2 ยังมีสถานะเป็น RC อยู่ก็ตาม แต่ความร้อนแรงของตัวเฟรมเวิร์กนั้นทำให้ผมพลาดไม่ได้ที่จะหยิบมาเขียนบทความ เพื่อให้เพื่อนๆได้เห็นภาพรวมก่อนเริ่มลงมือใช้ Angular2 บทความนี้จึงจะพาเพื่อนๆนั่งรถไฟตะลุยสวนสัตว์ เพื่อไปรู้จักกับองค์ประกอบคร่าวๆและเหล่าผองเพื่อนร่วมกรงขังสัตว์ของ Angular2

สารบัญ

รู้จัก Angular2 กันซะหน่อย

ในเอกสารของ Angular2 นิยามตัวเองว่าเป็น แพลตฟอร์มการพัฒนาแอพพลิเคชันบนมือถือ เดสก์ทอป และเว็บ ฟังแล้วยิ่งใหญ่ดีเนอะ Google ผู้อยู่เบื้องหลังการพัฒนากำลังจะสื่อว่า เค้าไม่ใช่เฟรมเวิร์คนะ แต่เค้ายิ่งใหญ่กว่านั้น เค้าเป็นแพลตฟอร์มเลยหละตะเอง… เมื่อพี่ใหญ่กล่าวอ้างแบบนั้น เรามาดูกันซิว่า Angular2 นั้นจะยิ่งใหญ่เท่ามังกรหรือจะสั้นจุ๊ดจู๋เป็นหนอนชาเขียวยอดใบชาโออิชิ

ความสามารถของ Angular2

เมื่อ Angular2 นั้นไม่ได้มีเพียงยักษ์ Google หนุนหลัง แต่ Microsoft ผู้พัฒนาภาษา TypeScript ก็ได้เข้ามามีส่วนร่วมพัฒนาด้วย เมื่อทั้งสองได้ฟีเจอริ่งกัน 🎉 … อะไรจะเกิดขึ้น

พัฒนาได้หลายแพลตฟอร์ม

Angular2 เกิดมาเพื่อทำงานได้หลายแพลตฟอร์ม ไลบรารี่คู่แข่งอย่าง React นั้นสามารถทำ Server-side Rendering ได้ แถมยังมี React Native ไว้สร้างแอพพลิเคชันบนสมาร์ทโฟนอีก ในขณะที่ Angular1 ได้แต่มองหน้าทำตาปริบๆ Google ไม่นิ่งเฉยจึงส่ง Angular2 ออกมากู้ทัพส่วนแบ่งทางการตลาด

ด้วยความสามารถของ Angular2 ตอนนี้ทำ Server-side Rendering ได้แล้ว ยังไม่พอ นอกจากจะมี ionic ไว้ทำ Hybrid แอพพลิเคชันบนมือถือแล้ว ยังสามารถทำ Native แอพพลิเคชันบนมือถือด้วย NativeScript และ React Native ได้อีกด้วย ช่างเป็นความสามารถครอบจักรวาลแบบ Universal ซะจริงๆ

ความเร็วที่เหนือกว่า

Google นั้นแม้จะเป็นเจ้าพ่อค้นหาข้อมูลที่ยิ่งใหญ่แล้ว ยังสวมวิญญาณเป็นเจ้าแม่คอนเซ็ปต์ Progressive Web Application และเทคโนโลยีเว็บที่ยิ่งใหญ่ด้วยเช่นกัน จึงไม่แปลกใจที่ Angular2 จะโกยความสามารถเหล่านี้เข้ามาไว้ในตัว ไม่ว่าจะเป็นความเร็วในการจัดการ DOM ที่เพิ่มขึ้นไม่น้อยหน้าไปกว่า Virtual DOM ของ React แล้ว รวมถึงการรีดประสิทธิภาพของเว็บด้วยการใช้ Web Workers

เครื่องมือที่เหนือชั้น

แม้ Angular2 จะเขียนด้วย Dart หรือ ES2015 ได้ แต่กระนั้น TypeScript ก็ยังคงเป็นพลเมืองชั้นหนึ่ง TypeScript นั้นเป็นภาษาที่พัฒนาโดย Microsoft เจ้าแห่ง IDE ในสามโลก ผู้พัฒนาและผลิต Visual Studio ที่เหล่าสาวก .Net ทุกวันนี้ยังคงใช้ลิขสิทธิ์พันธ์ทิพย์หรือไม่งั้นก็ไลเซนต์ torrent กันอยู่เลย จึงมั่นใจได้ว่าภาษา TypeScript จาก Microsoft จะทำงานได้ดีบน IDE แน่น่อน

สถาปัตยกรรมแอพพลิเคชันของ Angular2

พอได้ยินคำว่าสถาปัตยกรรมแล้วฟังดูยิ่งใหญ่นะครับ ความจริงแล้วแอพพลิเคชันหนึ่งๆของ Angular2 ประกอบด้วยชิ้นส่วนเหล่านี้มาเรียงร้อยต่อกันแค่นั้นเอง พิจารณาภาพข้างล่างจากเว็บไซต์ทางการของ Angular2 ครับ

Architecture Overview

อย่าพึ่งตกใจกับเส้นที่โยงใยเหล่านี้นะครับ เราค่อยๆมาชำแหละแต่ละส่วนออกดูกันดีกว่า

แรกเริ่มกับ Modules

Module

โลกความเป็นจริง สิ่งของที่กระจัดกระจายย่อมเข้าถึงยากและเข้าใจยากด้วยใช่ไหมครับ แต่ถ้าเราจัดกลุ่มให้ของเหล่านั้น โดยนำสิ่งที่เกี่ยวข้องกันมาใส่ในกล่องใบเดียวกันพร้อมแปะป้ายบอกประเภทไว้ เมื่อเราต้องการของประเภทไหนก็ไปควานหาจากกล่องตามป้ายที่ระบุประเภท แบบนี้ก็จะจัดการสิ่งของได้ง่าย เข้าถึงได้ง่าย และดูแลรักษาง่าย (ความรักก็เช่นกัน… แง แมวพิมพ์)ในโลกของการพัฒนาแอพพลิเคชัน เรามักจับของที่เกี่ยวข้องกันรวมกลุ่มเช่นเดียวกัน เรียกการรวมกลุ่มนี้ว่าโมดูล (module)

Angular2 สนับสนุนแนวคิดเรื่องโมดูล เราจะพบเห็นประโยค import/export ที่อยู่ใน ES2015 บ่อยมากใน Angular2 ตัวอย่างเช่นถ้าเรามีคอมโพแนนท์ตัวหนึ่ง (ยังไม่ต้องสนใจนะครับว่าคอมโพแนนท์คืออะไร เราจะพูดถึงกันเร็วๆนี้) คอมโพแนนท์ก็ถือเป็นโมดูลซะแล้ว เพราะมันเป็นสิ่งที่สถาปนาขึ้นมาเพื่อรวมความสัมพันธ์ของข้อมูล เหตุการณ์และพฤติกรรมที่สัมพันธ์กับ template (ยังไม่ได้พูดถึงเช่นกัน) เราจึงสามารถเปิดฝากล่องเพื่อให้โลกภายนอกเข้ามาขุดคุ้ยของในกล่องคือคอมโพแนนท์ของเราได้เช่นกัน การเปิดฝากล่องนี้ก็คือการใช้ export ที่แปลว่าส่งออกนั่นแหละ

export class AppComponent { }

ถัดมาคือ Libraries

Libraries

ใน Angular2 นั้นจะมีกลุ่มของโมดูลที่มีไว้เพื่อให้โมดูลอื่นใช้งาน มันจึงเป็นโมดูลที่มีความสามารถพิเศษที่จำเป็นต่อโมดูลอื่น โมดูลประเภทนี้เราเรียกว่า Libraries

Libraries ที่มาพร้อมกับ Angular2 เพื่อเสริมทัพให้เราสามารถสร้างสื่งต่างๆในแอพพลิเคชันของเราได้มักมีชื่อขึ้นต้นด้วย @angular เช่น @angular/core, @angular/common และ @angular/router เป็นต้น ดังนั้นเมื่อเราต้องการใช้ Libraries เหล่านี้ อย่าลืมที่จะสรรเสริญชื่อพวกมันในประโยค import

// นำเข้า Component จาก Library ชื่อ @angular/core
import { Component } from '@angular/core'

สวัสดีเราคือพระเอกชื่อ Component ไง

เราเขียนโปรแกรมเพื่ออะไร? เพื่อให้มีข้อความหรืออะไรซักอย่างแสดงผลออกมาที่หน้าจอใช่ไหมครับ นั่นละฮะงานของพระเอกเรา คอมโพแนนท์เป็นสิ่งที่ใช้ควบคุมการแสดงผลทางหน้าจอ หรือพูดอีกนัยยะคือใช้ควบคุม view ก็ได้ เช่น ต้องการให้แสดงผลรายชื่อของหนังสือกี่เล่มด้วยเงื่อนไขอะไรบ้างออกทางหน้าจอ โดยการทำงานของพระเอกเรานั้นจะสัมพันธ์กับหลายส่วนมาก เช่น template และ service และนี่หละครับพระเอกของเรา… คอมโพแนนท์

// อ่านโค๊ดแบบผ่านๆเอาคอนเซ็ปต์นะครับ
// อย่าพยายามเข้าใจทุกอย่าง เพราะตอนนี้เรายังไม่ได้สอนการเขียน Angular2
export class BookListComponent implements OnInit {
  // คอมโพแนนท์เราเก็บรายชื่อหนังสือเอาไว้
  // อีกซักครู่เราจะส่งรายชื่อหนังสือนี้ไปแสดงผลที่หน้าจอ
  // รออ่านต่อที่หัวข้อ template
  books: Book[]

  // เรามี service ชื่อ BookService ซึ่งมีหน้าที่หาข้อมูลหนังสือมาให้เรา
  // เราไม่สนใจว่า service จะทำงานยังไง มันอาจหารายชื่อหนังสือจากการเรียก API ก็ได้
  // แต่ที่เราสนใจคือ service นี้จะเป็นช่องทางให้เราได้รายชื่อหนังสือทั้งหมดมา
  // เรื่องของ service ติดตามต่อที่หัวข้อ service ครับ
  constructor(private service: BookService) {
  
  }
  
  ngOnInit() {
    // เมื่อคอมโพแนนท์นี้ถูกสร้างเรียบร้อย
    // เราจะได้รายการหนังสือทั้งหมดมาจากการเรียก getBooks()
    // ของ BookService
    this.books = this.service.getBooks()
  }
}

Template: เราคือผู้เชี่ยวชาญการแสดงผล

Template

คอมโพแนนท์นั้นควบคุมการแสดงผลของ view ครับ แต่ template คือตัวแสดงผลเองเลย จะบอกว่ามันคือส่วนของ view ก็ได้เช่นกัน

Template นั้นมีลักษณะเป็นฝาแฝดร่วมสะดือเดียวกับ HTML เลยครับ แต่สิ่งที่มีมากกว่าคือ template นั้นเพิ่มไวยากรณ์พิเศษที่เรียกว่า Angular’s Template Syntax เข้ามาด้วย เช่น

<h2>Book List</h1>
<ul>
  <li *ngFor="let book of books">
    {{book.title}}
  </li>
</ul>

เราใช้ ngFor เพื่อวนลูปรอบ books จากนั้นจึงเลือกเอาเฉพาะ title ของหนังสือมาแสดงใน li ผ่าน {{book.title}} พวกนี้หละครับคือ template syntax

Metadata เพื่อการตั้งค่าที่สมบูรณ์

Metadata

เพื่อนๆลองย้อนกลับไปดูโค๊ดคอมโพแนนท์ของเราที่ชื่อว่า BookListComponent ดูซิครับ จะเห็นว่าคอมโพแนนท์ของเรานั้นเป็นเพียงคลาสธรรมดามาก แต่ Angular2 ของเราจะรู้ได้อย่างไรว่านี่คือคอมโพแนนท์? ที่สำคัญคือมันจะรู้ได้ยังไงว่าจะหยิบ template ตัวไหนมาแสดงผลดี?

เพื่อทำโค๊ดให้สมบูรณ์เราจึงต้องมี metadata เพื่อใช้บอกหรืออธิบายว่าคลาสธรรมดาของเรานั้นทำอะไรและมีการตั้งค่าอะไรบ้าง

@Component ({
  selector: 'book-list',
  templateUrl: 'app/book-list.component.html',
  providers: [BookService]
})
export class BookListComponent implements OnInit {

}

จากตัวอย่างข้างต้น @Component เรียกว่า annotation หรือ decorator เป็นสิ่งที่เราแปะไว้เหนือคลาสเพื่อระบุพฤติกรรมพิเศษให้กับคลาสนี้ โดยมี metadata ที่เราใช้ตั้งค่าให้คือ selector, templateUrl และ providers ทั้งสามตัวนี้เพื่อนๆยังไม่ต้องสนใจนะครับ เก็บความสงสัยไว้ก่อน เราจะได้ลงรายละเอียดกันเมื่อเริ่มเขียนโค๊ดครับ

ผูกข้อมูลเข้าหากันด้วย Data Binding

Data Binding

การแสดงผลของ HTML หรือ template นั้นเกี่ยวข้องกับ DOM ใช่ไหมครับ Angular2 จึงต้องมีกลไกจัดการว่าเมื่อคอมโพแนนท์มีข้อมูลที่อัพเดทใหม่จะให้แสดงผลลง DOM ผ่าน template อย่างไร หรือเมื่อมีเหตุการณ์เกิดขึ้นเช่นการคลิกปุ่ม จะให้คอมโพแนนท์รับรู้แล้วทำสิ่งใดต่อ พวกนี้คือพฤติกรรมที่ Angular2 จัดการผ่านสิ่งที่เรียกว่า Data Binding หรือการผูกข้อมูลระหว่างคอมโพแนนท์ เทมเพลต และ DOM เข้าด้วยกัน

ย้อนกลับไปดูที่เทมเพลตของเรากันครับ สังเกตเห็น {{book.title}} ใช่ไหมครับ นั่นละฮะ data binding ในที่นี้เป็นการผูกข้อมูล title ของหนังสือซึ่งเป็นข้อมูลในคอมโพแนนท์ เข้ากับเทมเพลตที่มีเป้าหมายสุดท้ายคือการแสดงผลลง DOM

Directives: ตัวช่วยแปลง DOM

Directive

จากหัวข้อเทมเพลต เพื่อนๆคงค้นพบกันแล้วใช่ไหมฮะว่าเทมเพลตของเรานั้นไม่ตายตัว ผลลัพธ์ที่ได้จากเทมเพลตขึ้นอยู่กับว่าในตอนนั้นเรามีข้อมูลเป็นอะไร จากตัวอย่างของเราเทมเพลตนั้นอ้างอิงถึง books เทมเพลตเราจึงแสดงผลออกหน้าจอแปรเปลี่ยนตามข้อมูลของ books ที่เรามี เมื่อ Angular2 ต้องการแสดงผลเทมเพลต มันจะอาศัยความช่วยเหลือของ directives ที่จะเป็นตัวบอกว่าให้แปลงเทมเพลตออกมาเป็น DOM หน้าตาอย่างไร

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

<h2>Book List</h1>
<ul>
  <li *ngFor="let book of books">
    {{book.title}}
  </li>
</ul>

ในความเป็นจริงแล้ว directives นั้นคือคลาสครับ แต่เป็นคลาสที่มี metadata ของ directive ที่ตั้งค่าไว้ผ่าน @Directive แต่เผอิญว่าคอมโพแนนท์ที่เราสร้างผ่านการจั่วหัวว่า @Component นั้นก็เป็น directive เช่นกัน แต่เป็น directive บวก template เราก็เลยได้ความสามารถของ template syntax มาใช้งานแบบ ngFor ไงหละ

Services: งานบริการต้องยกให้เรา

Service

คอมโพแนนท์ BookListComponent ของเรา ถ้าจะรวมทุกสรรพสิ่งไว้ในตัวมันก็คงไม่ต่างอะไรกับจับฉ่ายที่ทำมันทุกอย่างเอาดีซักอย่างไม่ได้ ดังนั้นเราจึงแยกงานดึงข้อมูลรายชื่อหนังสือจาก API ออกมาเป็นเซอร์วิสชื่อ BookService เพื่อให้มันจัดการงานด้านนี้แทน เป็นการแบ่งเบาภาระของคอมโพแนนท์ไปในตัว

เซอร์วิสคือก้อนข้อมูล ฟังก์ชัน หรืออะไรก็ได้ ขอเพียงให้เกิดมาเพื่อทำงานเฉพาะทางอะไรซักอย่างและทำให้ดีด้วยนะเออ งานที่สามารถเป็นเซอร์วิสได้ เช่น

  • Logging Service สำหรับพิมพ์ log
  • Data Service สำหรับดึงข้อมูลจากเซิร์ฟเวอร์อย่างเช่น BookService ของเรา

Dependency Injection: ข้าตามมาหลอนเจ้าจาก Angular1 ไงหละ หึหึ

Dependency Injection

เมื่อเซอร์วิสเป็นที่ต้องการ Angular2 จึงต้องมีกลไกในการส่งเซอร์วิสไปให้ถึงมือคอมโพแนนท์ที่ต้องการใช้งานเซอร์วิสนั้นๆ พิจารณา constructor ของ BookListComponent ต่อไปนี้อีกครั้งครับ

export class BookListComponent implements OnInit {
  constructor(private service: BookService) {
  
  }
}

เมื่อ Angular2 สร้างคอมโพแนนท์มันจะดูซิว่าคอมโพแนนท์นั้นต้องการเซอร์วิสอะไรบ้าง จากตัวอย่างของเรามีเพียง BookService เท่านั้นที่คอมโพแนนท์นี้ต้องการ Angular2 จะเริ่มโวยวายถามหาเซอร์วิสตัวนี้จาก injector

Injector จะเป็นผู้ดูแลกล่องเก็บเซอร์วิสใบหนึ่ง (container) ถ้าเซอร์วิสที่ Angular2 ร้องขอนั้นมีอยู่ใน container แล้วมันก็จะคืนเซอร์วิสนั้นจากในกล่องกลับไป หากไม่มี injector จะสร้างเซอร์วิสนั้นขึ้นมาใหม่ โยนใส่กล่อง พร้อมทั้งคืนค่าเซอร์วิสนั้นกลับไปให้ Angular2 และนี่หละครับคือกลไกของ dependency injection

สรุป

Architecture Overview

ประมวลจากทั้งหมดที่เราคุยกันในบทความนี้จึงสรุปได้ว่า Angular2 นั้นทำให้ชีวิตเราง่ายขึ้นด้วยการให้เราเขียน HTML ที่เป็น template ควบคู่กับไวยากรณ์ที่เพิ่มเติมตามแบบฉบับของ Angular2 มีคอมโพแนนท์เป็นตัวช่วยควบคุม template เหล่านั้นอีกที โดยอาศัย application logic จากเซอร์วิส

เอกสารอ้างอิง

ARCHITECTURE OVERVIEW. Retrieved July, 15, 2016, from https://angular.io/docs/ts/latest/guide/architecture.html


แสดงความคิดเห็นของคุณ


Cha Aramnongjoyปีที่แล้ว

ถ้าเปรียบเทียบกับโลกของ mvc m คือ service v คือ template c คือ component ผมเข้าใจถูกไหมครับ?

ข้อความตอบกลับ
Nuttavut Thongjorปีที่แล้ว

ใช่ครับ แม้ ng2 จะเป็น component-based architecture แต่ถ้าเปรียบกลับไปเป็น MVC ก็จะได้โครงสร้างคล้ายๆกันคือ service - Model component template - View component class - Controller


Boy Thanakornปีที่แล้ว

เยี่ยมมากครับ ผมเคยลอง ionic2 กับ Angluar 2RC5 แบบงูๆปลาๆ พอเจอบทความนี้ช่วยได้เยอะเลยครับ ขอบคุณครับ


Zaichone Dragnueปีที่แล้ว

ติดตามอยู่นะครับ เด๋วแวะมาอ่านเป็นพักๆ ^_^

ข้อความตอบกลับ
Nuttavut Thongjorปีที่แล้ว

ขอบคุณครับ 😃


Nuttavut Thongjorปีที่แล้ว

สวัสดีครับคุณ Worapon Olanwanitchakul

ไม่ทราบว่าได้ลอง https://github.com/angular/universal รึเปล่าครับ


Worapon Olanwanitchakulปีที่แล้ว

Server rendering Angular2 นี้ต้องทำยังไงหรอครับ ตัวที่ผมใช้อยู่มัน error อยู่ใน github ก็บอกว่ายังเป็นปัญหาแก้ไม่ได้ เลยไม่กล้าใช้เลยครับ ขอบคุณล่วงหน้าครับ