[ASP.NET Core MVC Workshop#03] การทำ Form พร้อม Validate และการ Insert ข้อมูลลง Database

ในบทความนี้เราจะสร้าง Form เพื่อทำการ Insert โดยที่มีการ Validate ด้วย ซึ่งจะนำ Source code จากบทความ [ASP.NET Core MVC Workshop#02] การ Select ข้อมูลจาก Database ด้วย Model และแสดงข้อมูลด้วย​ Table มาทำต่อ หรือจะ Download ที่ Github ก็ได้ครับ


สร้าง Form และทำความเข้าใจ HTML Control

ก่อนอื่นให้ Add >> New File ที่ TrainingDotNetCoreMVC/Views/Movie สร้างไฟล์ที่เป็น Razor Page ชื่อไฟล์ว่า Create

และใส่ Code ดังภาพด้านซ้ายเพื่อทดสอบค่าเริ่มต้น

จากนั้นเปิดไฟล์ TrainingDotNetCoreMVC/Controllers/MovieController.cs ขึ้นมาและใส่ Code ที่เป็น Method ที่ชื่อว่า Create() ดังภาพ

จากนั้นให้ Run Project และเข้าไปที่ URL: http://localhost:xxx/movie/create เพื่อทดสอบดูความถูกต้องของการสร้างหน้า Form

จากนั้นก่อนจะสร้างหน้าตา Form อยากให้ท่านผู้อ่านเข้าใจ HTML Control ก่อนครับ โดยการเพิ่ม Code ในส่วนของไฟล์ root/Views/Movie/Create.cshtml ดังภาพ

และทำการ Inspect ที่ Web Browser

จะพบผลลัพธ์ว่ามันเหมือนกัน (ต่างกันเล็กน้อย)

ที่ให้ทำแบบนี้เพื่ออยากให้ท่านผู้อ่านเข้าใจว่าการใช้ Html.xxx นั้น สุดท้ายจะออกมารูปแบบไหน และให้เห็นอีกด้วยว่าการใช้ tag HTML เดิม ก็ทำงานร่วมกันได้ เพราะฉะนั้นสามารถใช้ได้ทั้งคู่ เลือกที่ถนัดได้เลยครับ

แต่ถ้าใช้ Html.xxx ของ .NET จะมีข้อดีอะไร ? เท่าที่ทราบคือ จะมี Helper dialog ขึ้นมาช่วย และเหมาะกับการใช้งาน Library ของตัว .NET เองมากกว่า(ข้อหลังเดี๋ยวมาดูตอนทำ Validation)

โดยท่านผู้อ่านสามารถดูเกี่ยวกับ HTML Helpers เพิ่มเติมได้ที่ลิงค์นี้ครับ

กลับมาที่การสร้าง Form ต่อไปให้ใส่ Code สำหรับ Form โดยเปิดไฟล์ 2 ไฟล์ คือ

  • root/Views/Shared/_Layout.cshtml
  • root/Views/Movie/Create.cshtml

เริ่มที่ _Layout.cshtml ให้เพิ่ม Code @RenderSection("Styles", required: false) ในบรรทัดนี้ครับ

อธิบายไฟล์ _Layout.cshtml

  • @RenderSection("Styles", required: false) ใส่ที่หน้าหลัก เพื่อที่จะให้หน้าลูก ๆ หรือหน้าที่เอาไปใช้ สามารถใส่ Code ใน Section นี้ได้

ต่อไปเป็นไฟล์ Create.cshtml ให้ใส่ Code ทั้งหมดเป็นแบบนี้ครับ

Create.cshtml

อธิบายไฟล์ Create.cshtml

  • @model เราสามารถผูก Model มาที่หน้า Form ได้เลย เผื่อสะดวกในการใช้งานว่า Control ตัวไหนเป็นของใครใน Model
  • @section Styles คือส่วนที่เราใส่ Code แล้วจะไปอยู่ในส่วนของ @RenderSection() ในหน้าหลัก(ในที่นี้คือ _Layout.cshtml นั่นเอง) และค่าที่ใส่ไปนั้น คือ css สำหรับการใช้ bootstrap-datetimepicker
  • @using (Html.BeginForm()) และ @Html.xxx(modal => modal.xxx)ทั้งหลาย คือ HTML tag ของ Web page razor ซึ่งมันจะเปลี่ยนเป็น HTML tag ธรรมดา (ที่เราได้ลองดูใน Inspect ใน Web browser) ส่วน model.xxx นั้น ตัว HTML tag จะผูกให้เราเลยครับ ตามชื่อ Attribute ของ Model เช่น model.title
  • @section Script เหมือนกับ@section Styles แต่เป็นเรื่องของ Script ซึ่งอันนี้มีส่วนของ bootstrap-datetimepicker และ moment.js
  • bootstrap-datetimepicker คือ ตัวเลือกวันที่และเวลา ซึ่งมีข้อแม้ว่าจะใช้ moment.js ด้วย ซึ่งทั้งสองคือตัวที่สร้างด้วย JavaScript ตัว datetimepicker เอามาจาก eonasdan.github.io ครับ ส่วนวิธีใช้คือไม่ได้ Download มา แต่เรียก Path ตรง ๆ เลยก็คือเป็นแบบ CDN (อ่านเพิ่มเติมเกี่ยวกับ CDN จาก blog ของคุณ Shin Ji ได้ที่ลิงค์นี้)
เพื่อไม่ให้เนื้อหายาวเกินไปจึงขออธิบายบางส่วนที่น่าจะเข้าใจยากเท่านั้น หากท่านผู้อ่านสงสัยตรงไหนเพิ่มเติม ให้ถามทิ้งไว้ได้เลยครับ

จากนั้นให้ลองรันดูผลลัพธ์ครับ จะได้ดังภาพด้านล่าง


Insert ข้อมูลลง Database

เพื่อความสะดวกการไปหน้า Form ให้เปิดไฟล์ root/Views/Movie/Index.cshtml และใส่ Code เพื่อลิงค์ไปหน้า Form ดังภาพ

จากนั้นไปที่ไฟล์ root/Controller/MovieController.cs และเพิ่ม Code ในส่วนของ Method Create() ที่เป็น HttpPost ดังภาพ

อธิบายไฟล์ root/Controller/MovieController.cs

  • [HttpPost] คือส่วนที่ระบุว่าให้เป็น Method Post นะ (อธิบายทำไม 😓)
  • IFormFile คือ Interface ที่ใช้กับไฟล์ที่ทำการ Upload โดยตัวแปรที่ชื่อว่า fileUpload ต้องตรงกับหน้า Form ที่เป็น <input type="file" name="fileUpload" id="fileUpload" />
  • Directory.Exits() และ Directory.CreateDirectory() ตรงตามชื่อเลยครับ คือตรวจสอบว่ามี Directory ตามที่ระบุ path ไปหรือไม่ และสั่งสร้าง Directory ตาม path ที่ระบุ
  • Path.GetExtension() คือ ส่วน Path.Combine() คือ
  • new FileStream() คือ และ fileUpload.CopyToAsync() คือ
  • db.Movie.Add() และ db.SaveChangesAsync()
  • RedirectToAction()

จากนั้นให้ลองทดสอบ Insert ที่หน้า Form ดูครับ จะพบว่าข้อมูลเข้าแล้วครับ

จะพบว่าการใช้ .NET Core สำหรับการทำงานบางอย่าง ยุ่งยากเล็กน้อยหากเทียบกับ .NET Framework อย่างเช่นการ Save ไฟล์ ซึ่งถ้าเป็น .NET Framework จะยังมี HttpPostedFileBase ที่สามารถสั่ง Save ไฟล์ได้เลย

ทำ Validation ให้กับ Form

จากขั้นตอน Insert ที่ได้ลองไปแล้ว แน่นอนว่าถ้ากรอกข้อมูลแบบผิด ๆ หรือส่งค่าที่เราไม่ต้องการ ย่อมทำให้มัน Save ข้อมูลไปที่ Database ด้วย เราจึงต้องมาทำ Validate ให้กับ Form เพื่อเอาเฉพาะข้อมูลที่เราสนใจนั่นเอง

เริ่มจากเตรียม Error message โดยใช้ @Html.ValidationMessageFor และ @Html.ValidationMessage ไปที่ไฟล์ Create.cshtml โดยจะใส่เพิ่มและได้ผลลัพธ์สุดท้ายดังนี้

Create.cshtml

@Html.ValidationMessageFor นั้นจะใช้สำหรับผูกกับ model ตามฟิลด์ที่เราสร้างขึ้นส่วน@Html.ValidationMessageไปที่ไฟล จะใช้สำหรับ Custom เอง โดยดูการ Custom ได้ที่ไฟล์ MovieController.cs ใน Step ต่อไป

จากนั้นให้เปิดไฟล์ MovieController.cs และใส่ Code ที่ใช้ if มา Check ข้อมูล โดยเพิ่มเข้าไปใน Method Create สุดท้ายจะได้ดังนี้

MovieController.cs

if (fileUpload == null) สำหรับตรวจสอบว่ามีการ Post file (เลือกไฟล์ส่งมาที่ Server) มาหรือไม่ 
if (model.duration < 1) คือ การเช็คว่ามีการกรอกค่า Duration (Default ของค่าคือ 0 ถ้าไม่มีการส่งค่ามาก็จะเป็น 0 ทำให้เช็คได้ว่าน้อยกว่า 1 นั่นเอง)
ส่วน if (ModelState.IsValid) มีเป็นการตรวจสอบที่ขึ้นตรงกับไฟล์ Model ครับว่า Validate อะไรไว้ ถ้าไม่เข้าผ่าน IsValid ก็จะเป็น false ทันที

ต่อไปเปิดไฟล์ MovieModel.cs ขึ้นมา และเพิ่ม [Required] ให้เป็นดังภาพด้านล่าง

MovieModel.cs

ซึ่ง [Required] ที่เพิ่มเข้ามา เป็นการระบุว่าเราต้องการค่านั่นเอง ในที่นี้เราได้เอาไปใช้กับไฟล์ Create.cshtml จึงทำให้ค่อนข้างสะดวกในการ Validate ว่าค่านั้นมีค่าหรือไม่

จากนั้นให้ลอง Run ทดสอบดูครับไปที่หน้า Create และลอง Submit แบบไม่ใส่ค่าอะไรจะได้ผลลัพธ์ดังภาพ

ใช้งานได้แล้วครับ แต่ !! อยากให้เข้าใจอีกเรื่องหนึ่งคือ การ Validate จากฝั่ง Client ซึ่งใช้ JavaScript

ขออธิบายเล็กน้อยว่า สิ่งที่เราทำอยู่เป็นการ Validate จากฝั่ง Server ซึ่งจะส่งค่าไปที่ฝั่ง Server และ Server ตรวจสอบ จากนั้นก็จะส่งคำตอบกลับมา จะประมาณนี้

แต่ถ้าทำ Validate จากฝั่ง Client ก็จะไม่ต้องส่งค่าไปตรวจสอบที่ Server แต่จะตรวจสอบเองเลย เป็นประมาณนี้ครับ

ข้อดีก็จะชัดเจนว่า ทำให้ภาระของ Server น้อยลง ซึ่งถ้าใช้แค่ไม่ถึง 10 เครื่อง มี User ไม่ถึง 10 คนทำการกรอกข้อมูลมาและต้อง Check Validate ก็ไม่เท่าไหร่ แต่ถ้าสมมติว่ามี 100 เครื่อง User 100 คน การทำ Validate ที่ Client ก็คงจะเป็นประโยชน์มากทีเดียว

แล้วทำอย่างไรหล่ะ ?

คำตอบคือใส่ Script 2 บรรทัดนี้

<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.16.0/jquery.validate.min.js"></script>   
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"></script>

เข้าไปที่ @section Scripts ของไฟล์ Create.cshtml ดังภาพ

จากนั้นลองทดสอบดู

จะพบว่าหน้าไม่ Reload แล้ว นั่นหมายความว่าเราทำ Validate ฝั่ง Client สำเร็จ แต่ว่าเรื่องของ Duration กับ รูปภาพมันไม่ทำให้นี่สิ

ก็เลยต้องทำเองครับใส่ Code JavaScript นี้

javaScript function

อธิบายซักเล็กน้อยว่าทำไมถึงใช้ selector ระบุแบบ span[data-valmsg-for="xxx" กำลังสงสัยหรือไม่ว่ามันมาจากไหน คำตอบคือมาจากการ Inspect ดูค่าว่าตัว@Html.ValidationMessage สุดท้ายแล้วมันถูก Gen เป็นอะไรนั่นเอง

และต่อไปเพิ่ม Code ที่ปุ่ม Submit ให้ใส่ onclick ตามนี้

<input onclick="return validateOnSubmit()" type="submit" value="Create" class="btn btn-primary" />

เข้าไปที่ @section Scripts ของไฟล์ Create.cshtml อีกครั้ง ดังภาพ

จากนั้นให้ลองทดสอบอีกครั้งจะได้ผลลัพธ์ดังภาพ

ถือว่าเป็นการทำ Validate form เสร็จสิ้น


สำหรับการสร้าง Form และ Insert ข้อมูลลง Database พร้อมทั้งทำ Form validation ด้วย ก็จบแต่เพียงเท่านี้ หากท่านผู้อ่านต้องการ Source code สามารถ Download ได้ที่ลิงค์นี้

ขอขอบคุณท่านผู้อ่านที่ติดตาม แล้วพบกันใหม่ใน #04 ครับ



หากท่านผู้อ่านมีคำถามหรือข้อสงสัย หรือมีคำแนะนำ ติชม สามารถติดต่อผู้เขียนได้เลยครับ

Reference