https://pixabay.com/en/zen-garden-meditation-monk-stones-2040340/

Single Page Applications (SPA)

Arkhom Khamthip
CheHipster

--

คือ web application ที่จะโหลด html เพียงตัวเดียว แต่สามารถตอบสนองการใช้งานของผู้ใช้ โดยจะสามารถเรียกใช้ html ตัวอื่นๆ แบบ dynamic ได้. สิ่งที่แตกต่างอย่างชัดเจนระหว่างเวปไซต์ทั่วไปและ SPA คือการลดจำนวนการรีเฟรชหน้าเวป. SPA ใช้ AJAX และ HTML5 ในการติดต่อไปที่ back-end servers เพื่อไปเอาข้อมูลมาแสดงผลที่หน้าเวป โดยที่ไม่ต้องทำการรีเฟรชเวปทั้งหมด ซึ่งกระบวนการในการ render เวปเพจ จะเกิดขึ้นที่ฝั่ง client เกือบทั้งหมด.

  • ข้อดีของ SPA
  1. เร็วกว่า เพราะการเปลี่ยน URL แต่ละครั้งจากผู้ใช้งาน ระบบไม่จำเป็นที่จะต้องไปทำงานที่ back-end servers แต่ระบบจะทำงานที่ client เพื่อทำการแสดงผลบนหน้าเวป.
  2. ใช้ bandwidth น้อย เพราะไม่ได้ส่ง html ทั้งหมดไป back-end servers ในการเปลี่ยน URL แต่ละครั้ง แต่จะทำการเรียก API แทน เพื่อให้ได้ผลลัพธ์ที่เพียงพอต่อการแสดงผลบนหน้าเวป.
  • ข้อเสียของ SPA
  1. browser ทำงานหนักขึ้นเพราะการประมวลผลเกิดขึ้นที่ client ซึ่งอาจจะเกิดปัญหาเรื่องของ performance.
  2. ควรคิดถึง 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/

--

--