การส่งข้อมูลไปยัง backend service ด้วย HttpClient ใน Angular ตอนที่ 3

เขียนเมื่อ 6 ปีก่อน โดย Ninenik Narkdee
angular httpclient httpclientmodule service api

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ angular httpclient httpclientmodule service api

ดูแล้ว 12,610 ครั้ง


สำหรับเนื้อหาในตอนที่ 3 ในการใช้งาน HttpClient นี้ เราจะมาดูในเรื่องการส่งข้อมูลไปยัง
backend service จะประยุกต์โดยใช้งานกับ form สำหรับส่งข้อมูล โดย ในการใช้งาน form ใน Angular 
นั้นจะมีอยู่ด้วยกัน 2 รูปแบบ คือ แบบ Template-driven Form และ Reactive Form สามารถทบทวน
ได้ที่บทความด้านล่าง
    เตรียมใช้งาน Template driven forms ใน Angular เบื้องต้น http://niik.in/774 
    การใช้งาน Reactive Form ด้วย FormBuilder ใน Angular http://niik.in/793 
 
ในที่นี้เราจะใช้งานรูปแบบ Template driven Form ในตัวอย่างนี้ เพราะใช้งานง่ายและไม่ซับซ้อนมากนัก โดยการใช้งาน
Template driven form นั้นจะมี ngForm และ ngModel มาช่วยในการจัดการ เราจะใช้การอ้างอิงตัวแปร
ผ่าน template reference variable หรือที่เรียกว่า template variable ที่กำหนด้วย # นำหน้า รูปแบบเช่น
 
<form #registForm="ngForm" >
<input type="text" ngModel name="fullname">
<button type="submit">Send</button>
</form>
 
การใช้งาน template driven form จะมีการใช้งาน ngForm กับ tag <form> อัตโนมัติ โดยเราไม่ต้องกำหนด
ngForm selector เข้าไปดังโค้ด แต่เนื่องจากเราต้องการนำค่าจาก ngForm มาใช้งานผ่านตัวแปร template variable
ที่ชื่อ registForm เราจึงกำหนดเป็น #registForm="ngForm" ตามรูปแบบข้างต้น โดยตัวแปร registForm จะได้ค่า
สำหรับจัดการข้อมูลในฟอร์มมาสองค่าคือ
 
registForm.valid มีค่าเป็น true ถ้าฟอร์มนี้ตรวจสอบความถูกต้องของข้อมูลในฟอร์แล้ว และ false ถ้าเป็นค่าตรงข้าม
registForm.value มีค่าเป็น object ของข้อมูลฟิลด์ต่างๆ ในฟอร์ม โดยฟิลด์ที่จะให้ข้อมูลต้องมีการกำหนด ngModel
 
อย่างในรูปแบบฟอร์มอย่างง่ายข้างต้น <input> element จะส่งค่าไปยัง registForm.value ก็ต่อเมื่อมีการกำหนด name
attribute และเรียกใช้งาน ngModel เป็นดังนี้
 
<input type="text" ngModel name="fullname">
 
ค่าของ registForm.value ก็จะเป็นในรูปแบบ 
 
{
	"fullname":""
}
 
เช่นเดียวกับ ngForm เราสามารถใช้งานตัวแปร template variable กับ ngModel เพื่ออ้างอืง element นั้นๆ ได้ดังนี้
 
<input type="text" ngModel #fullname="ngModel" name="fullname">
 
เราสามารถอ้างอิงค่าและความถูกต้องของ input type=text ผ่านตัวแปร fullname ที่กำหนดด้วย #fullname ได้เหมือน
กับกรณืของ ngForm คือ
 
fullname.valid ตรวจสอบความถูกต้องของข้อมูลใน element นั้น true / false เช่นว่า element นี้กำหนด required 
attribute เข้าไป 
fullname.value ค่าของข้อมูลที่เรากรอกลงไปใน input type=textbox
 
ค่า valid และ value ที่อ้างอิงผ่านตัวแปรนี้ เราสามารถเอาไปใช้งานในการกำหนด css class ให้แสดงรูปแบบการแจ้งเตือน
ของ element นั้นว่า ถูกต้องหรือไม่ได้
 
เวลาเราส่งข้อมูล เราก็จะส่งข้อมูล registForm นี้เข้าไปใช้งาน โดยสามารถจะส่งแบบโดยใช้ตัวแปร registForm หรือจะส่งผ่านค่า registForm.value
ไปใช้งานก็ได้
 
เรามาเริ่มการใช้งานการส่งข้อมูลหลังจากทบทวน template driven form เบื้องต้นเล็กน้อยไปแล้วกันเลย
โดยเราสร้างเป็น module ใหม่ขึ้นมา ชื่อว่า register หรือหน้าฟอร์มสำหรับการสมัครสมาชิก ด้วยคำสั่ง ดังนี้
 
C:\projects\httpclient>ng g module register --routing
C:\projects\httpclient>ng g c register/register --flat
 
จะได้ไฟล์ต่างๆ สำหรับใช้งานดังรูปดังนี้
 
 


 
 
กำหนด path ในการใช้งาน register form ดังนี้
 
ไฟล์ register-routing.module.ts
 
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { RegisterComponent } from './register.component';

const registerRoutes: Routes = [
  {
    path:'register',
    component:RegisterComponent
  }
];

@NgModule({
  imports: [RouterModule.forChild(registerRoutes)],
  exports: [RouterModule]
})
export class RegisterRoutingModule { }
 
และกำหนดการเรียกใช้งาน FormsModule ใน RegisterModule เพื่อให้สามารถใช้งาน template driven form
 
ไฟล์ register.module.ts
 
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule }   from '@angular/forms';

import { RegisterRoutingModule } from './register-routing.module';
import { RegisterComponent } from './register.component';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    RegisterRoutingModule
  ],
  declarations: [RegisterComponent]
})
export class RegisterModule { }
 
ในกรณีที่เราต้องการใช้งาน form แบบ template driven form ใน module ใด เราต้องทำการ import FormsModule
เข้ามาใช้งาน ก่อนเสมอ ในกรณีนี้เราสามารถ import มาใช้งานเฉพาะใน RegisterModule ที่เดียวก็ได้ หรือถ้าเราอาจจะ
มีการใช้งานใน Component อืนๆใน App ที่ไม่มีการแยกเป็น Module เราก็สามารถ import FormsModule นี้ในไฟล์
app.module.ts ด้วยก็ได้ เราจะขอ import ไปใช้งานใน AppModule หลักด้วย โดยไว้ต่อท้ายจาก BrowserModule 
 
ไฟล์ app.module.ts
 
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule }   from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { ArticleModule } from './article/article.module';
import { RegisterModule } from './register/register.module';

import { AppComponent } from './app.component';
import { PagenofoundComponent } from './pagenofound/pagenofound.component';
import { HomeComponent } from './home/home.component';

@NgModule({
  declarations: [
    AppComponent,
    PagenofoundComponent,
    HomeComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    ArticleModule,
    RegisterModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
 
 
เรียบร้อยแล้ว อย่าลืมเพิ่มเมนูมายัง register path ในไฟล์ app.component.html ดังนี้
 
ไฟล์ app.component.html
 
<nav class="navbar navbar-expand-sm navbar-light bg-light">
    <a class="navbar-brand" [routerLink]="['/home']">Angular</a>
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" [routerLink]="['/home']">Home <span class="sr-only">
            (current)
          </span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link"  [routerLink]="['/article']">Article</a>
        </li>
        <li class="nav-item">
            <a class="nav-link"  [routerLink]="['/register']">Register</a>
          </li>        
      </ul>
    </div>
  </nav>
  
  <div class="container">
    <router-outlet></router-outlet>
  </div>
 
 
ตอนนี้เราพร้อมสำหรับการมาจัดการหน้าฟอร์ม และการส่งข้อมูลจากฟอร์มด้วย HttpClient โดยใช้ post() method 
แล้ว ให้เราสร้างฟอร์มสำหรับสมัครสมาชิกอย่างง่าย โดยเรามีการใช้งาน css class ของ bootstrap ค่า css class ต่างๆ 
จะไม่เกี่ยวกับ angular ตรงนี้ระวังจะสับสน โดยฟอร์มเราจะมีแค่ ช่องกรอก fullname และปุ่มส่งค่า ก่อนเท่านั้น ดังนี้
 
ไฟล์ register.component.html
 
<p>  register works!</p>
<pre>{{ registForm.value | json }}</pre>
<form (ngSubmit)="onSubmit(registForm)" #registForm="ngForm" novalidate>
  <div class="form-group row">
    <label for="fullname" class="col-3 col-form-label text-right">
        ชื่อ นามสกุล
    </label>
    <div class="col-6">
      <input type="text" class="form-control is-invalid" ngModel #fullname="ngModel" 
      name="fullname" id="fullname" 
      [class.is-valid]="fullname.valid" [class.is-invalid]="!fullname.valid" 
      placeholder="Fullname" required>
      <div class="invalid-feedback">
        Please provide a valid Fullname.
      </div>      
    </div>
    <div class="offset-3"></div>
  </div>  
  <div class="form-row">
    <div class="col-9 offset-3">
      <button type="submit" [disabled]="!registForm.valid" class="btn btn-primary">
        สมัครสมาชิก
      </button>
    </div>
  </div>    
</form>
<br>
 
เรามาดูผลลัพธ์และการทำงานกันอธิบายตามรูป
 


 
 
เมื่อยังไม่กรอกข้อมูลใดๆ ก็จะมีรูปแบบการแจ้งผ่าน css ของ bootstrap ตามรูป
 
 


 
 
และเมื่อมีการกรอกข้อมูลลงไป css ก็จะปรับไปตามที่กำหนด แสดงถึงการตรวจสอบความถูกต้องของ
ข้อมูลแล้ว ในตัวอย่างเราแสดงค่าของ registForm จะเห็นว่า มีค่าข้อมูลตามที่เรากรอก ค่านี้จะถูกส่งไปใช้งาน
กับ HttpClient ผ่าน onSubmit() ฟังก์ชั่น  เราจะไม่ขออธิบายเกี่ยวกับการใช้งาน template driven เพิ่มเติมมากนัก
ยังไงสามารถกลับไปย้อนอ่านได้ที่บทความเพิ่มเติม ที่ได้บอกไปแล้วข้างต้น
 
มาดูในส่วนของการจัดการกับข้อมูลจากฟอร์ม และการส่งค่าด้วย HttpClient post() method ในไฟล์ 
register.component.ts โดยในการทดสอบ เราจะส่งไปยัง server จำลองผ่าน url 
http://localhost/demo/show_data.php
 
ไฟล์ register.component.ts
 
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { HttpClient, HttpHeaders, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  // selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  public urlApi:string = "http://localhost/demo/";
  public responseValue:any;

  constructor(
    private http:HttpClient,
    private route: ActivatedRoute,
    private router:Router
  ) { }  

  onSubmit(f:any){
    let data = f.value;
    this.http.post(this.urlApi+'show_data.php',data)    
    .subscribe(result =>{
      this.responseValue = result;
      console.log(result);
    },
    ( err:HttpErrorResponse ) => {
      // กรณี error
      if (err.error instanceof Error) {
        // กรณี error ฝั่งผู้ใช้งาน หรือ การเชื่อมต่อเกิด error ขึ้น
        console.log('An error occurred:', err.error.message);
      }else{ // กรณี error ฝั่ง server ไม่พบไฟล์ ,server error 
        console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
      }       
    });
  }

  ngOnInit() {

  }

}
 
 
จะเห็นว่าในส่วนของ onSubmit() ฟังก์ชั่นที่เราสร้างขึ้น มี parameter ที่รับค่า registForm โดยเราใช้
ตัวแปร f เพื่ออ้างอิงค่าที่ส่งเข้ามา onSubmit(f:any)  แต่ค่าที่เราจะส่งไปยัง backend service คือ f.value หรือ object
ของข้อมูลในฟอร์ม เรากำหนดตัวแปร data มารับค่า จากนั้นส่งเข้าไปยัง http.post() ตามรูปแบบ
 
this.http.post(this.urlApi+'show_data.php',data)    
 
จะเห็นว่าถึงจะเป็นการ ส่งข้อมูล แต่เราก็ยังต้องมีการเรียกใช้ subscribe() ไม่เช่นนั้นก็จะไม่มีการทำงานใดๆ เกิดขึ้น
ทั้งนี้เพราะค่าที่ได้จากการใช้งาน http.post() เป็นข้อมูลแบบ Observable ในโค้ด เราให้คืนค่าข้อมูลกลับมาถ้า
มีการส่งข้อมูลสำเร็จ หรือกรณี error ก็มีการตรวจสอบ errror ที่เกิดขึ้นว่ามาจากสาเหตุใด
 
ในการใช้งาน http.post() เพื่อส่งข้อมูลนั้น จะคล้ายกับการทำงานในรูปแบบ ajax ที่เราคุ้นตามาบ้างแล้ว แต่ก็ไม่เหมือน
เสียทีเดียว โดยเฉพาะในส่วนของไฟล์รับค่า ในการส่งค่าเมื่อเรากดปุ่ม submit จะมีการส่งค่าไปสองครั้ง ดูรูปจาก
console ด้านล่างประกอบ
 
 


 
 
จะเห็นว่ามีการเรียกไปยังไฟล์ show_data.php สองครั้ง ครั้งแรกเป็นการส่ง method OPTION ส่วนครั้งที่สอง
เป็นการส่งข้อมูลด้วย method POST ดังนั้นในไฟล์ show_data.php ถ้าเราไม่มีการตรวจจับค่าที่ส่งเข้ามาด้วยเงื่อนไข
ใดๆ ก็จะทำให้มีการทำงานในไฟล์นั้นสองครั้ง ดังนั้นเราต้องกำหนดในไฟล์ show_data.php ในรูปแบบดังนี้
หมายเหตุ: ไฟล์ show_data.php เป็นไฟล์ใน backend service ไม่ได้อยู่ใน angular project ของเรา ไฟล์ตัวอย่าง
ต่อไปนี้จะอยู่ server จำลอง
 
ไฟล์ show_data.php
 
<?php
header('Access-Control-Allow-Origin: *'); 
header('Access-Control-Allow-Headers: Content-Type,X-Custom-Header'); 
header('Content-type: application/json');
//require_once("dbconnect.php");
$jsonData = array();	
if($_SERVER['REQUEST_METHOD'] == "GET") {
	echo json_encode($jsonData);
}elseif($_SERVER['REQUEST_METHOD'] == "OPTIONS"){
	echo json_encode($jsonData);
}elseif($_SERVER['REQUEST_METHOD'] == "POST"){
	$json = file_get_contents('php://input');
	$postData = json_decode($json, TRUE);
/*	$mysqli->query(
	"
	INSERT INTO tbl_data SET 
	data_text='".$postData['fullname']."'
	"
	);	*/
	echo json_encode(array(
		"statusCode"=>"200 OK",
		"statusMessage"=>"Successful"	
	));
}else{
    header("HTTP/1.1 403 Access Forbidden");
	echo json_encode(array(
		"statusCode"=>"403 Access Forbidden",
		"statusMessage"=>"Fail"
	));
}
?>
 
จะเห็นว่าในการส่งข้อมูลแบบ POST เรากำหนดเงื่อนไขการทำงานไว้เฉพาะกรณีที่มีการส่งข้อมูลมาด้วย
method POST เท่านั้น ถึงจะมีการส่งค่า method OPTION เข้ามาครั้งแรก ก็ไม่มีการทำงานใดๆ ซึ่งหาก
เราไม่มีการกำหนดในรูปแบบนี้ และมีการให้ไฟล์ show_data.php ทำงานใดๆ ก็จะกลายเป็นการทำงานซ้ำสองครั้ง
 
ใน method POST ที่เป็นเงื่อนไขการรับค่า เราสามารถอ้างอิงตัวแปร POST ที่ส่งเข้ามาผ่าน ตัวแปร $postData
เช่น $postData['fullname'] ก็คือ ชื่อจาก input type=text ที่ชื่อ fullname ส่งมาใช้งาน ในตัวอย่างได้ comment
ส่วนที่ใช้งานกับ การเชื่อมต่อฐานข้อมูลไว้ สามารถไปประยุกต์เพิ่มเติมได้
    และในการส่งค่ากลับ เรามีการกำหนดรูปแบบของข้อมูลเป็นแบบ json string data ค่าเหล่านี้จะถูกใช้ในฝั่ง 
application front-end เพื่อใช้ในการทำงานอื่น เช่นกรณีส่งค่ากลับไปว่าทำงานสำเร็จเป็น successful เราก็สามารถ
ใช้ค่าที่คืนกลับไปนี้ ไปเป็นเงื่อนไขในการทำงานต่อ
 
เราสามารถทดสอบดูค่าที่ส่งจากฟอร์มเข้าไปในไฟล์ show_data.php โดยกำหนดเพิ่มไปดังนี้
 
echo json_encode(array(
    "statusCode"=>"200 OK",
    "statusMessage"=>"Successful",
    "postData"=>$json
));
 
 
เรามาดูโค้ดการประยุกต์แบบเต็มของไฟล์ register.component.ts และ register.component.html
 
ไฟล์ register.component.html
 
<p>  register works!</p>
<pre>{{ responseValue | json }}</pre>
<pre>{{ registForm.value | json }}</pre>
<form (ngSubmit)="onSubmit(registForm)" #registForm="ngForm" novalidate>
  <div class="form-group row">
    <label for="fullname" class="col-3 col-form-label text-right">
        ชื่อ นามสกุล
    </label>
    <div class="col-6">
      <input type="text" class="form-control is-invalid" ngModel #fullname="ngModel" name="fullname" id="fullname" 
      [class.is-valid]="fullname.valid" [class.is-invalid]="!fullname.valid" 
      placeholder="Fullname" required>
      <div class="invalid-feedback">
        Please provide a valid Fullname.
      </div>      
    </div>
    <div class="offset-3"></div>
  </div>  
  <div class="form-group row">
    <label for="email" class="col-3 col-form-label text-right">
        อีเมล
    </label>
    <div class="col-6">
      <input type="email" class="form-control is-invalid" ngModel #email="ngModel" name="email" id="email" 
      [class.is-valid]="email.valid" [class.is-invalid]="!email.valid" 
      placeholder="Email" required>
      <div class="invalid-feedback">
        Please provide a valid Email.
      </div>           
    </div>
    <div class="offset-3"></div>
  </div>    
  <div class="form-group row">
    <div class="col-3 text-right">เพศ</div>
    <div class="col-9">
        <label class="custom-control custom-radio">
          <input id="radio1" ngModel #gender="ngModel" name="gendar" value="ชาย" type="radio" 
          [class.is-invalid]="!gender.valid" 
          class="custom-control-input" required>
          <span class="custom-control-indicator"></span>
          <span class="custom-control-description">ชาย</span>
        </label>
        <label class="custom-control custom-radio">
          <input id="radio2" ngModel #gender="ngModel" name="gendar" value="หญิง" type="radio" 
          [class.is-invalid]="!gender.valid" 
          class="custom-control-input" required>  
          <span class="custom-control-indicator"></span>
          <span class="custom-control-description">หญิง</span>
        </label>      
    </div>
  </div>   
  <div class="form-group row">
    <div class="col-3 text-right">ความสนใจ</div>
    <div class="col-9">
        <div class="custom-controls-stacked">
          <label *ngFor="let hobby of hobbies;let i=index;" class="custom-control custom-checkbox">
            <input type="checkbox" ngModel name="hobby_{{i}}" [value]="hobby" class="custom-control-input">
            <span class="custom-control-indicator"></span>
            <span class="custom-control-description">{{ hobby }}</span>
          </label>          
        </div>
    </div>
  </div>     
  <div class="form-group row">
    <label for="address" class="col-3 col-form-label text-right">
        ที่อยู่
    </label>
    <div class="col-7">
      <textarea class="form-control is-invalid" ngModel #address="ngModel" name="address" id="address" rows="3" 
      [class.is-valid]="address.valid" [class.is-invalid]="!address.valid" 
      placeholder="Address" required></textarea>
      <div class="invalid-feedback">
        Please provide a valid Address.
      </div>        
    </div>
    <div class="offset-2"></div>
  </div>    
  <div class="form-group row">
    <label for="province" class="col-3 col-form-label text-right">
        จังหวัด
    </label>
    <div class="col-9">
      <select class="custom-select is-invalid" ngModel #province="ngModel" name="province" id="province" 
      [class.is-valid]="province.valid" [class.is-invalid]="!province.valid" 
      required>
        <option value="">เลือกจังหวัด</option>
        <option *ngFor="let province of provinces" [value]="province">
          {{ province }}</option>
      </select>
      <div class="invalid-feedback">
        Please provide a valid Province.
      </div>         
    </div>
  </div>     
  <div class="form-group row">
    <label for="zipcode" class="col-3 col-form-label text-right">
        รหัสไปรษณีย์
    </label>
    <div class="col-3">
      <input type="text" class="form-control is-invalid" 
      ngModel #zipcode="ngModel" name="zipcode" id="zipcode" 
      [class.is-valid]="zipcode.valid" [class.is-invalid]="!zipcode.valid" 
      placeholder="Zip Code" required>
      <div class="invalid-feedback" [hidden]="zipcode.valid">
        Please provide a valid Zip Code.
      </div>        
    </div>
    <div class="offset-6"></div>
  </div> 
  <div class="form-row">
    <div class="col-9 offset-3">
      <button type="submit" [disabled]="!registForm.valid" class="btn btn-primary">
	  สมัครสมาชิก</button>
    </div>
  </div>    
</form>
<br>
 
ไฟล์ register.component.ts
 
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { HttpClient, HttpHeaders, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  // selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  public provinces:string[] = ['กรุงเทพฯ','นนทบุรี'];
  public hobbies:string[] = ['ออกกำลังกาย','อ่านหนังสือ'];
  public urlApi:string = "http://localhost/demo/";
  public responseValue:any;  

  constructor(
    private http:HttpClient,
    private route: ActivatedRoute,
    private router:Router
  ) { }  

  onSubmit(f:any){
    let data = f.value;
    this.http.post(this.urlApi+'show_data.php',data)    
    .subscribe(result =>{
      this.responseValue = result;
      console.log(result);
    },
    ( err:HttpErrorResponse ) => {
      // กรณี error
      if (err.error instanceof Error) {
        // กรณี error ฝั่งผู้ใช้งาน หรือ การเชื่อมต่อเกิด error ขึ้น
        console.log('An error occurred:', err.error.message);
      }else{ // กรณี error ฝั่ง server ไม่พบไฟล์ ,server error 
        console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
      }       
    });
  }

  ngOnInit() {

  }

}
 
ดูตัวอย่างผลลัพธ์ได้ที่ Demo ด้านล่าง
 
เนื้อหาในตอนนี้ เราเห็นวิธีการใช้งาน http.post() เบื้องต้นไปแล้ว ในตอนหน้าเราจะมาลองรายละเอียดการใช้งาน
เพิ่มเติม รอติดตาม
 




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



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









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









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





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







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