Devahoy Logo
PublishedAt

Web Development

เริ่มต้นเขียน Angular2 กันดีกว่า

เริ่มต้นเขียน Angular2 กันดีกว่า

เมื่อเร็วๆนี้ Angular ได้ปล่อยตัว Angular 4 ออกมา ซึ่งจริงๆแล้ว มันก็ไม่ได้แตกต่างกับ Angular 2 เท่าไหร่นัก เป็นเพียงแค่ Improve Feature เล็กๆน้อยๆ ไม่ได้ Major change แบบตอน Angular 1 มา Angular 2 นั่นเอง

วันนี้ก็เลยมานำเสนอบทความการเขียน Angular 2 ฉบับเริ่มต้นกันครับ ซึ่งตัว Angular 2 นั้นเขียนด้วย TypeScript และตัวอย่างของวันนี้จะเป็นเว็บแบบ Single Page Application ที่เอาไว้แสดงข้อมูล Heroes ของเกม Dota 2 ครับ

หน้าตาของ App เมื่อเขียนเสร็จก็จะได้ประมาณรูปด้านล่างครับ

Example Screen

จะเรียก Angular เวอร์ชั่น 2 ขึ้นไปว่า Angular ส่วน Angular 1.x จะเรียกว่า AngularJS

Enviroments

ในตัวอย่างนี้จะใช้ Angular-cli ในการสร้างโปรเจ็ค รวมถึงการสร้าง Component และเขียนด้วย TypeScript

ฉะนั้นสิ่งที่ควรมีก็คือ

หากยังไม่ได้ติดตั้ง Node.js สามารถอ่านได้จากบทความนี้ Node.js คืออะไร ? + เริ่มต้นใช้งาน Node.js

Step 1 : Installation

Terminal window
$ npm install typescript -g
$ npm install angular-cli -g

บน Mac OS X หรือ Linux อาจจะต้องติดตั้ง Watchman เพิ่มเติม (ตัว Watchman เอาไว้สำหรับ Monitoring file ของ AngularCLI)

Terminal window
$ brew install watchman

หากไม่มี Homebrew หรือยังไม่รู้จัก อ่านบทความนี้ครับ Homebrew คืออะไร? + สอนวิธีใช้งาน

Step 2 : Create Project

เริ่มต้นสร้างโปรเจ็ค Angular ด้วยการใช้ angular-cli จะใช้คำสั่ง ng new <ชื่อโปรเจ็ค> ครับ

Terminal window
$ ng new awesome-app
installing ng2
create .editorconfig
create README.md
create src/app/app.component.css
create src/app/app.component.html
create src/app/app.component.spec.ts
create src/app/app.component.ts
create src/app/app.module.ts
create src/assets/.gitkeep
create src/environments/environment.prod.ts
create src/environments/environment.ts
create src/favicon.ico
create src/index.html
create src/main.ts
create src/polyfills.ts
create src/styles.css
create src/test.ts
create src/tsconfig.json
create angular-cli.json
create e2e/app.e2e-spec.ts
create e2e/app.po.ts
create e2e/tsconfig.json
create .gitignore
create karma.conf.js
create package.json
create protractor.conf.js
create tslint.json
Successfully initialized git.
Installing packages for tooling via npm.
Project 'awesome-app' successfully created.

ไฟล์และ dependencies ต่างๆจะถูกติดตั้งด้วย angular-cli

เมื่อสร้างโปรเจ็คด้วย angular-cli เสร็จแล้ว ก็ลองรัน server ดูด้วยคำสั่ง ng serve

Terminal window
$ cd awesome-app
$ ng serve

เข้าเว็บผ่าน http://localhost:4200/ จะเห็นหน้าเว็บ Angular

Angular HelloWorld

Step 3 : Project Structure

เมื่อลอง browse file ในโฟลเดอร์ src จะเห็นว่ามีไฟล์และโฟลเดอร์ต่างๆดังด้านล่างนี้

Terminal window
├── app/
│   ├── app.component.css
│   ├── app.component.html
│   ├── app.component.spec.ts
│   ├── app.component.ts
│   └── app.module.ts
├── assets/
├── environments/
│   ├── environment.prod.ts
│   └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
└── tsconfig.json

ลองดูที่ไฟล์ src/app/app.component.html

1
<!doctype html>
2
<html>
3
<head>
4
<meta charset="utf-8" />
5
<title>AwesomeApp</title>
6
<base href="/" />
7
8
<meta name="viewport" content="width=device-width, initial-scale=1" />
9
<link rel="icon" type="image/x-icon" href="favicon.ico" />
10
</head>
11
<body>
12
<app-root>Loading...</app-root>
13
</body>
14
</html>

จะสังเกตเห็น tag <app-root> ใน Element <app-root>Loading...</app-root> ซึ่งหากใน AngularJS (Angular 1) จะเรียกว่า Diretives ส่วนใน Angular (Angular 2)เราจะเรียกมันใหม่ว่า Component

แล้วทีนี้ Component ใน Angular มันสร้างมาได้ยังไง? ลองเปิดไฟล์ src/app/app.component.ts ขึ้นมา

1
import { Component } from '@angular/core'
2
3
@Component({
4
selector: 'app-root',
5
templateUrl: './app.component.html',
6
styleUrls: ['./app.component.css']
7
})
8
export class AppComponent {
9
title = 'app works!'
10
}

จากโค๊ดด้านบน มีการ import Component จากโมดูล @angular/core และประกาศ Component โดยมี property คือ

  • selector : ชื่อ component ที่เราต้องการ เช่น app-root เวลาใช้งานก็คือ <app-root></app-root>
  • templateUrl: Path ไฟล์ html ที่เราต้องการให้ Component นี้มัน render
  • styleUrls: สามารถกำหนด stylesheet เฉพาะ component ได้
  • template: หรือหากไม่ระบุ url ก็สามารถใส่ HTML tag ลงไปในนี้ได้เลย

ซึ่งหากสังเกตแล้ว จริงๆ 1 component ของ Angular จะมีด้วยกัน 4 ไฟล์ คือ

  • app.component.css : เอาไว้สำหรับจัดการ stylesheet ของ component นั้น
  • app.component.html : ไฟล์ html template ของ Component
  • app.component.spec.ts : ไฟล์สำหรับ test
  • app.component.ts : ไฟล์สำหรับ implement logic component

Step 4 : Generate component

เนื่องจากว่าตอนเราสร้างโปรเจ็คด้วย Angular-CLI app.component จะถูกสร้างมาให้อัตโนมัติอยู่แล้ว ทีนี้เราลองมาสร้าง Component เพิ่มด้วยคำสั่ง CLI กันครับ

คำสั่งก็ง่ายๆเลยคือ ng generate component <ชื่อ component> เช่น

Terminal window
ng generate component about
installing component
create src/app/about/about.component.css
create src/app/about/about.component.html
create src/app/about/about.component.spec.ts
create src/app/about/about.component.ts
update src/app/app.module.ts

หรือจะใช้คำสั่งแบบสั้นๆ ก็ได้ คือ ng g c about

จะเห็นว่าไฟล์ถูกสร้างมา 4 ไฟล์ รูปแบบคล้ายๆการสร้าง 1 Component และมีอัพเดทไฟล์ src/app/app.module.ts

ซึ่งหากเราเปิดไฟล์ app.module.ts ดูจะพบว่า

1
import { BrowserModule } from '@angular/platform-browser'
2
import { NgModule } from '@angular/core'
3
import { FormsModule } from '@angular/forms'
4
import { HttpModule } from '@angular/http'
5
6
import { AppComponent } from './app.component'
7
import { AboutComponent } from './about/about.component'
8
9
@NgModule({
10
declarations: [AppComponent, AboutComponent],
11
imports: [BrowserModule, FormsModule, HttpModule],
12
providers: [],
13
bootstrap: [AppComponent]
14
})
15
export class AppModule {}

สิ่งสำคัญมีอยู่ 3 ส่วนครับ คือ

  1. declarations : เอาไว้กำหนด Component ของเราให้ใช้ได้ภายใน app ถ้าไม่ใส่ในส่วนนี้ Component ที่เราสร้างมาตัว angular จะไม่รู้จัก
  2. imports : สำหรับ import โมดูลต่างๆเพื่อเอาไว้ใช้ในโปรเจ็ค เช่น HttpModule สำหรับ GET/POST Http เป็นต้น
  3. providers : ตัวนี้จะเป็นสำหรับส่วนที่เอาไว้ประกาศ เวลาเรามี Service

Step 5: Create Routing

ต่อมาเป็นการสร้าง Routing เพื่อจะกำหนดว่า เวลาเราเข้า path นี้ จะให้มันไปไหน เช่น เข้า /about จะให้แสดงหน้า About เข้าหน้า /heroes ก็แสดงรายชื่อ Heroes เป็นต้น

ตัวอย่างการกำหนด Routing โดย App นี้จะแบ่งออกเป็น 3 หน้าคือ

  • Home : เป็นหน้าแรก
  • Heroes : แสดงรายชื่อ Heroes
  • About : หน้ารายละเอียด

ซึ่งทัง้สามหน้า จะเป็นแค่ Static website มีเพียงแค่ Heroes ที่จะต้องดึง Service API นั่นเอง

สร้างไฟล์ชื่อ app-routing.module.ts ขึ้นมา

1
import { NgModule } from '@angular/core'
2
import { RouterModule, Routes } from '@angular/router'
3
import { AboutComponent } from './about/about.component'
4
5
const routes: Routes = [
6
{
7
path: 'about',
8
component: AboutComponent
9
}
10
]
11
12
@NgModule({
13
imports: [RouterModule.forRoot(routes)],
14
exports: [RouterModule]
15
})
16
export class AppRoutingModule {}

โดย Angular จะมาพร้อมโมดูล RouterModule และ Routes ให้เราไว้สำหรับ Config Routing อยู่แล้ว เราก็กำหนดได้เลยว่า path = about ให้มันแสดง component ชื่อ AboutComponent

ตัว path ไม่ต้องใส่ slash (/)

กลับไปที่ไฟล์ app.module.ts ทำการ import RoutingModule ที่เราสร้างนี้ไปด้วย

1
import { BrowserModule } from '@angular/platform-browser'
2
import { NgModule } from '@angular/core'
3
import { FormsModule } from '@angular/forms'
4
import { HttpModule } from '@angular/http'
5
6
import { AppComponent } from './app.component'
7
import { AboutComponent } from './about/about.component'
8
import { AppRoutingModule } from './app-routing.module'
9
10
@NgModule({
11
declarations: [AppComponent, AboutComponent],
12
imports: [BrowserModule, FormsModule, HttpModule, AppRoutingModule],
13
providers: [],
14
bootstrap: [AppComponent]
15
})
16
export class AppModule {}

และที่ไฟล์ app.component.html ให้เราเพิ่ม tag <router-outlet></router-outlet> เพื่อให้ Angular Router ทำงานนั่นเอง เมื่อเวลา Routing มันแมตกับ path ที่เรากำหนด ตัว Component ก็จะ replace ข้อมูลแทนแท็กนี้นั่นเอง

1
<div class="container">
2
<router-outlet></router-outlet>
3
</div>

ต่อมา Generate หน้า Home และ Hero List สำหรับเป็น Component และ map routing ไว้ก่อน (ยังไม่มีคอนเท้น)

Terminal window
ng g c home
ng g c hero-list

และทำการเพิ่ม Routing ในไฟล์ app-routing.module.ts

1
import { NgModule } from '@angular/core'
2
import { RouterModule, Routes } from '@angular/router'
3
4
import { AboutComponent } from './about/about.component'
5
import { HomeComponent } from './home/home.component'
6
import { HeroListComponent } from './hero-list/hero-list.component'
7
8
const routes: Routes = [
9
{
10
path: '',
11
component: HomeComponent
12
},
13
{
14
path: 'about',
15
component: AboutComponent
16
},
17
{
18
path: 'heroes',
19
component: HeroListComponent
20
},
21
{
22
path: '**',
23
redirectTo: '/',
24
pathMatch: 'full'
25
}
26
]
27
28
@NgModule({
29
imports: [RouterModule.forRoot(routes)],
30
exports: [RouterModule]
31
})
32
export class AppRoutingModule {}

โดย path: '' คือจะเป็นหน้า Home และ path: '**' คือไม่เจอ path ใดๆเลย จะให้มัน redirect ไปที่ Home นั่นเอง

ตอนนี้ App เราก็มี 3 หน้าแล้ว คือ

Step 6: Services

ต่อมาส่วน Service ที่เราใช้ request เพื่อจะได้ข้อมูล Heroes แบบทั้งหมด และอีก request สำหรับ Hero id นั้นๆ

ไฟล์ชื่อ hero.service.ts

1
import { Injectable } from '@angular/core';
2
import { Http } from '@angular/http';
3
import 'rxjs/add/operator/map';
4
5
@Injectable()
6
export class HeroService {
7
8
constructor(private http: Http) { }
9
10
findAll() {
11
return this.http.get('http://localhost:5000/api/heroes')
12
.map(res => res.json());
13
}
14
15
findOne(id) {
16
return this.http.get(`http://localhost:5000/api/heroes/${id}`)
17
.map(res => res.json());
18
}
19
20
}

จะเห็นว่าโค๊ดด้านบน มีการ import { Http } from '@angular/http'; ซึ่งเป็น module สำหรับ Http ในส่วนของ

1
;`constructor(private http:Http) {}`

จะมีค่าเท่ากับเราประกาศ

1
let http;
2
3
constructor(http: Http) {
4
this.http = http;
5
}

คือเราทำการประกาศตัวแปร http ใน constructor ได้เลย

ต่อมาไฟล์ hero-list/hero-list.component.ts

1
import { Component, OnInit } from '@angular/core';
2
import { HeroService } from '../hero.service';
3
4
@Component({
5
selector: 'app-hero-list',
6
templateUrl: './hero-list.component.html',
7
styleUrls: ['./hero-list.component.scss']
8
})
9
export class HeroListComponent implements OnInit {
10
11
heroes: any = [];
12
13
constructor(private heroService: HeroService) { }
14
15
ngOnInit() {
16
this.heroService.findAll().subscribe(response => {
17
this.heroes = response.data;
18
});
19
}
20
21
}

ด้านบน เรา Inject HeroService ที่สร้างไว้ มาไว้ในนี้ผ่านตัวแปร heroService จากนั้นก็สามารถ call method findAll ได้เลย

แต่จะเห็นว่ามีส่วน .subscribe() ตรงนี้ทำหน้าที่คล้ายๆกับ Promise().then() คือเมื่อได้ response มันก็จะ ทำงานในส่วนของ subscribe() ซึ่งในที่นี้คือ นำค่า response ไปใส่ไว้ตัวแปรชื่อ heroes

เช่นเดียวกันกับ hero/hero.component.ts จะ Implement คล้ายๆกัน เพียงแต่ ส่วนที่เป็น /heroes/:id จะต้องใช้ ActivatedRoute ในการหา params ดังโค๊ดด้านล่าง

1
import { Component, OnInit } from '@angular/core';
2
import { ActivatedRoute } from '@angular/router';
3
import { HeroService } from '../hero.service';
4
5
@Component({
6
selector: 'app-hero',
7
templateUrl: './hero.component.html',
8
styleUrls: ['./hero.component.scss']
9
})
10
export class HeroComponent implements OnInit {
11
12
hero: any;
13
14
constructor(private route: ActivatedRoute, private heroService: HeroService) { }
15
16
ngOnInit() {
17
18
this.route.params.subscribe(params => {
19
const id = params['id'];
20
this.heroService.findOne(id).subscribe(response => {
21
this.hero = response.data;
22
});
23
24
});
25
26
}
27
28
like() {
29
console.log('like');
30
}
31
32
}

สุดท้าย เพิ่ม HeroService ไปในส่วน provider ของ app.module.ts ด้วย

1
import { BrowserModule } from '@angular/platform-browser'
2
import { NgModule } from '@angular/core'
3
import { FormsModule } from '@angular/forms'
4
import { HttpModule } from '@angular/http'
5
6
import { AppComponent } from './app.component'
7
import { AboutComponent } from './about/about.component'
8
import { AppRoutingModule } from './app-routing.module'
9
import { HeroComponent } from './hero/hero.component'
10
import { HomeComponent } from './home/home.component'
11
import { HeroListComponent } from './hero-list/hero-list.component'
12
import { HeroService } from './hero.service'
13
14
@NgModule({
15
declarations: [AppComponent, AboutComponent, HeroComponent, HomeComponent, HeroListComponent],
16
imports: [BrowserModule, FormsModule, HttpModule, AppRoutingModule],
17
providers: [HeroService],
18
bootstrap: [AppComponent]
19
})
20
export class AppModule {}

Step 7: Add HTML Markup

ต่อมาผมทำการเพิ่ม HTML Markup ของแต่ละหน้าเริ่มจากไฟล์ index.html ทำการเพิ่ม CSS ของ Bulma

1
<!doctype html>
2
<html>
3
<head>
4
<meta charset="utf-8" />
5
<title>AwesomeApp</title>
6
<base href="/" />
7
8
<meta name="viewport" content="width=device-width, initial-scale=1" />
9
<link rel="icon" type="image/x-icon" href="favicon.ico" />
10
<link
11
rel="stylesheet"
12
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.2/css/bulma.css"
13
/>
14
</head>
15
<body>
16
<app-root>Loading...</app-root>
17
</body>
18
</html>

ต่อมาไฟล์ app.component.html ก็จะเพิ่มส่วน Navbar เพื่อจะได้ Link ไปหน้าต่างๆได้

1
<div class="container">
2
<nav class="nav">
3
<div class="nav-left">
4
<a class="nav-item" routerLinkActive="active" routerLink="/">Home</a>
5
</div>
6
7
<div id="nav-menu" class="nav-right nav-menu">
8
<a class="nav-item" routerLinkActive="active" routerLink="/heroes"> Heroes </a>
9
<a class="nav-item" routerLinkActive="active" routerLink="/about"> About </a>
10
</div>
11
</nav>
12
<router-outlet></router-outlet>
13
</div>

ซึ่ง HTML Tag ด้านบน เราจะเห็น routerLink และ routerLinkActive ซึ่งเป็นส่วนของ Angular Router โดย routerLink เอาไว้สำหรับบอกว่า Link นี้ จะไปไหน โดยมันจะไป map กับที่เรา config ไว้ใน app-routing.module.ts นั่นเอง

ไฟล์ app.component.css (บทความใช้ .scss)

1
body {
2
background-color: #ccc;
3
}
4
5
nav {
6
margin-bottom: 20px;
7
}

ต่อมา ส่วนหน้า About ก็ใส่ Content เพิ่มเข้าไป

1
<div class="tile is-ancestor">
2
<div class="tile is-half is-vertical is-parent">
3
<div class="tile is-child box">
4
<p class="title">About</p>
5
6
<div class="content">
7
<p>This is demo application built with Angular 2</p>
8
<p></p>
9
<ul>
10
<li>
11
<a
12
href="https://www.eventbrite.co.uk/e/building-spa-with-nodejs-and-angular2-tickets-32402850799"
13
>Building SPA with Hapi.js and Angular 2</a
14
>
15
</li>
16
<li>
17
<a href="https://github.com/Phonbopit/node-x-angular">Source code on Github</a>
18
</li>
19
</ul>
20
</div>
21
</div>
22
</div>
23
<div class="tile is-half is-parent">
24
<div class="tile is-child box">
25
<p class="title">Credit</p>
26
27
<div class="content">
28
<ul>
29
<li><a href="http://bulma.io/">Bulma</a> for CSS Framework</li>
30
<li><a href="https://www.opendota.com/">OpenDota API</a> for mock data</li>
31
</ul>
32
</div>
33
</div>
34
</div>
35
</div>

หน้า hero-list/hero-list.component.html ก็เช่นเดียวกัน

1
<div class="columns is-multiline ">
2
<div class="column is-one-quarter" *ngFor="let hero of heroes">
3
<div class="card">
4
<div class="card-image">
5
<figure class="image is-4by3">
6
<img src="https://api.opendota.com{{hero.img}}" alt="Icon" />
7
</figure>
8
</div>
9
<div class="card-content">
10
<div class="media">
11
<div class="media-content">
12
<p class="title is-4">
13
<a routerLink="/heroes/{{hero.id}}" class="is-success">{{hero.localized_name}}</a>
14
</p>
15
<p class="subtitle is-6">{{hero.name}}</p>
16
</div>
17
</div>
18
</div>
19
</div>
20
</div>
21
</div>

และสุดท้ายหน้า Hero Detail hero/hero.component.ts

1
<div class="card">
2
<div class="card-image">
3
<figure class="image is-4by3">
4
<img src="https://api.opendota.com{{hero?.img}}" alt="Icon" />
5
</figure>
6
</div>
7
<div class="card-content">
8
<div class="media">
9
<div class="media-content">
10
<p class="title is-4">{{hero?.localized_name}}</p>
11
<p class="subtitle is-6">{{hero?.name}}</p>
12
</div>
13
</div>
14
</div>
15
</div>
16
17
<hr />
18
19
<div class="columns">
20
<div class="column">
21
<a class="button is-outlined" routerLink="/heroes">Back</a>
22
</div>
23
<div class="column">
24
<a class="button is-outlined" (click)="like()">Like</a>
25
</div>
26
</div>

Step 8: Backend

สำหรับส่วน Backend จะไม่อธิบายถึงละกันครับ เพราะว่าอยู่นอก scope ของบทความ ซึ่งสามารถดูได้จาก Link นี้ https://github.com/Phonbopit/node-x-angular/tree/master/server

ซึ่งเมื่อเราทำการรัน Server หรือ มี response จาก backend ตัวหน้าเว็บที่เราทำไว้ก็จะสามารถใช้งานได้นั่นเอง

เป็นอันเรียบร้อย :)

Conclusion

บทความนี้ก็เป็นคล้ายๆ Walkthrough สำหรับทำ Single Page Appliction ด้วย Angular 2 หรือ 4 แบบง่ายๆนะครับ อาจจะไม่ได้เจาะลึกลงไปในแต่ละรายละเอียด ซึ่งอยากให้มองเห็นภาพรวมแล้วก็ไปต่อยอดศึกษาเพิ่มเติมเอาเองนะครับ เช่น Angular Router ว่ามันทำอะไรได้บ้าง หรือ RxJS หรือ Observable มันใช้ยังไง ทำอะไรอีกได้บ้าง เป็นต้น หวังว่าบทความนี้จะมีประโยชน์กับผู้ที่กำลังสนใจ Angular นะครับ

สุดท้าย Source Code สามารถดูได้จาก Link ด้านล่างเลยครับ

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts