Single Page Applications (SPA)
คือ web application ที่จะโหลด html เพียงตัวเดียว แต่สามารถตอบสนองการใช้งานของผู้ใช้ โดยจะสามารถเรียกใช้ html ตัวอื่นๆ แบบ dynamic ได้. สิ่งที่แตกต่างอย่างชัดเจนระหว่างเวปไซต์ทั่วไปและ SPA คือการลดจำนวนการรีเฟรชหน้าเวป. SPA ใช้ AJAX และ HTML5 ในการติดต่อไปที่ back-end servers เพื่อไปเอาข้อมูลมาแสดงผลที่หน้าเวป โดยที่ไม่ต้องทำการรีเฟรชเวปทั้งหมด ซึ่งกระบวนการในการ render เวปเพจ จะเกิดขึ้นที่ฝั่ง client เกือบทั้งหมด.
- ข้อดีของ SPA
- เร็วกว่า เพราะการเปลี่ยน URL แต่ละครั้งจากผู้ใช้งาน ระบบไม่จำเป็นที่จะต้องไปทำงานที่ back-end servers แต่ระบบจะทำงานที่ client เพื่อทำการแสดงผลบนหน้าเวป.
- ใช้ bandwidth น้อย เพราะไม่ได้ส่ง html ทั้งหมดไป back-end servers ในการเปลี่ยน URL แต่ละครั้ง แต่จะทำการเรียก API แทน เพื่อให้ได้ผลลัพธ์ที่เพียงพอต่อการแสดงผลบนหน้าเวป.
- ข้อเสียของ SPA
- browser ทำงานหนักขึ้นเพราะการประมวลผลเกิดขึ้นที่ client ซึ่งอาจจะเกิดปัญหาเรื่องของ performance.
- ควรคิดถึง SEO ด้วยเพราะ search engine และ social media อาจจะไม่ได้ผลดีเท่าที่ควร อ่านเพิ่มเติมได้จากโพสของจารบอย แห่ง Wongnai.
Angular มีโมดูลให้เราสร้าง SPA ที่มีชื่อว่า Component Router ซึ่งเราจะได้ลองเขียนเล่นดู
รถเข็นใส่ของ
เราจะเพิ่มระบบรถเข็นเข้าไปในเวปของเราในโพสที่แล้ว ซึ่งมีการทำงานคือ ผู้ใช้งานจะกดปุ่มรถเข็น แล้วระบบจะพาไปยังหน้าแสดงสินค้าในรถเข็น และกดปุ่ม back เพื่อกลับมาที่หน้าแสดงสินค้า.
- Route Configuration
เป็นการกำหนดว่าถ้ามีการเปลี่ยนเปลง URL ของ browser เราจะให้ Angular มันจะแสดงอะไรต่อไป ตัวอย่างข้างล่างนี้ เราจะกำหนดไว้ 3 route คือ ‘’, item และ cart.
//app-route.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';import { ItemComponent } from './item.component';
import { CartComponent } from './cart.component';const routes: Routes = [
{ path: '', redirectTo '/item', pathMatch: 'full' },
{ path: 'item', component: ItemComponent },
{ path: 'cart', component: CartComponent }
];@NgModule({
imports: [ RouterModule.forRoot(routes, {useHash: true}) ],
exports: [ RouterModule ]
})
export class AppRouteModule {}
ในส่วนของการ import เราจะต้องใช้ Routes, RouteModule
ภายใน Routes
เป็นการกำหนด URL เพื่อไปเรียก component
เช่น หาก URL มีค่าเป็น /#/item ระบบจะไปเรียกให้ ItemComponent ทำงาน ในที่นี่ก็จะแสดงรายการสินค้า หรือ หาก URL ไม่ได้ใส่อะไรเลย มันจะ redirect ไปที่ item โดยที่ค่าของ URL จะต้อง match ทั้งหมด.
หลังจากนั้นนำตัวแปร routes
ไปให้ RouterModule.forRoot เพื่อให้ Angular ทำการ configuration
สำหรับ
{useHash: true}
จะอธิบายในภายหลัง ซึ่งมันเป็นการบอกว่าเราใช้ Path Location Strategies ตอนนี้ URL ของเราจะถูกใส่เครื่องหมาย # ไว้ข้างหน้าเสมอเช่น /#/cart
- สร้าง CartComponent ใหม่ cart.component.ts
ตอนนี้เราจะสร้าง CartComponent แบบยังไม่ต้องทำอะไรมาก แค่มีปุ่ม back เพื่อกลับไปยังหน้าแสดงสินค้า.
// cart.component.ts
import { Component } from '@angular/core'
import { Location } from '@angular/common';@Component({
selector: 'my-cart',
templateUrl: './src/cart.component.html',
})
export class CartComponent {
constructor(private location: Location) {}
goBack() : void {
this.location.back();
}
}
เราเพิ่ม method goBack() : void ซึ่งไม่รับ parameter และไม่ส่งค่าใดๆ กลับออกไป. สำหรับ this.location.back()
เราใช้ this อ้างถึง instance คลาสนี้เพื่อเรียกใช้ location.back()
สำหรับ location เราได้ทำการ inject เข้ามาผ่าน constructor.
อ่านเพิ่มเติมเรื่องของ DI (Dependency Injection) ได้ที่นี่
- สร้าง cart.component.html
การที่เราจะเรียก method goBack() จาก html เราจะต้องทำการ binding ไปที่ component เราใช้ (click)="goBack()"
ซึ่งเมื่อมีการกดปุ่ม มันจะไปเรียก method goBack ใน CartComponent. Angular มีการ binding มากมายอ่านเพิ่มเติมที่นี่
// cart.component.html
<div class="card card-block">
<h4 class="card-title">รายการที่ซื้อ</h4>
<button type="button" class="btn" (click)="goBack()">
<i class="fa fa-arrow-left fa-lg" aria-hidden="true">Back</i>
</button>
</div>
- สร้าง app.component.ts
โพสก่อนหน้านี้ เราใช้ ItemComponent เป็น root component แต่ตอนนี้เราจะให้ AppComponent เป็น root แทนเพราะ ItemComponent และ CartComponent จะถูก AppComponent เรียกใช้
// app.component.ts
import { Component } from '@angular/core';@Component({
selector: 'my-app',
templateUrl: "src/app.component.html"
})
export class AppComponent {}
สร้าง app.component.html สำหรับแสดงผล
// app.component.html
<div>
<button type="submit"
[routerLink]="['/cart']"
class="btn btn-primary">
<i class="fa fa-shopping-cart fa-lg" aria-hidden="true"></i>
<span class="badge badge-secondary">0</span>
</button>
</div>
<p></p>
<router-outlet></router-outlet>
[routerLink]
เป็นตัวอย่างการใ่ช้งาน directive เพื่อไปใช้งาน route ที่เราได้เซ็ทไว้ซึ่งจะทำการ โหลด CartComponent เข้ามาทำงาน
- นำ AppComponent และ AppRouteComponent ไปใส่ไว้ใน AppModule
//our root app module
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';import { AppComponent } from './app.component';
import { ItemComponent } from './item.component';
import { CartComponent } from './cart.component';import { AppRouteModule } from './app-route.module';@NgModule({
imports: [
BrowserModule,
AppRouteModule
],
declarations: [
AppComponent,
ItemComponent,
CartComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
โครงสร้างของโปรเจคจะออกมาในรูปแบบนี้
โค๊ดสามารถดูได้จาก https://embed.plnkr.co/OwerChol92gEdCECvJaU/
References:
https://codecraft.tv/courses/angular/quickstart/overview/
https://angular.io/
https://jhipster.github.io/