ตอนที่ 3 จะเป็นเนื้อหา เกี่ยวกับการจัดการกับฟอร์ม ในลักษณะ
การเพิ่มข้อมูล ลบ แก้ไข แสดงข้อมูล และการค้นหา ในตอนนี้ จะมีการ
เรียกใช้ module ของ angularjs เพิ่มเติม
รูปแบบ การเรียนรู้ ก็จะยึด ตัวอย่าง จากเว็บไซต์ angularjs
ดูเนื้อหาตอนก่อนหน้าได้ที่
สร้าง todo app กับการศึกษา angularjs ตอนที่ 2
https://www.ninenik.com/content.php?arti_id=515 via @ninenik
เนื้อหาตอนนี้ จะมีการใช้งาน bootstrap css เพื่อให้หน้าตาการแสดงผลออกมาสวยงาม
ดาวน์โหลดได้ที่ เลือกดาวน์โหลด ตรงคำว่า download bootstrap
แตกไฟล์ แล้ว copy เฉพาะโฟลเดอร์ css และ fonts มาใช้งาน
โดยให้สร้าง โฟลเดอร์ bootstrap แล้ว copy โฟลเดอร์ css และ fonts ของ bootstrap มาไว้ด้านใน
\bootstrap\css
\bootstrap\fonts
index.html
โค้ดไฟล์แรก index.html
<!DOCTYPE html> <html ng-app="project"> <head> <meta charset="utf-8" /> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.17/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.17/angular-resource.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.17/angular-route.min.js"></script> <script src='https://cdn.firebase.com/js/client/1.0.15/firebase.js'></script> <script src="https://cdn.firebase.com/libs/angularfire/0.8.0/angularfire.min.js"></script> <script src="project.js"></script> <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css" /> <title>My Learn AngularJs 3</title> </head> <body> <br> <div class="container"> <h2>JavaScript Projects</h2> <div ng-view></div> </div> </body> </html>
1. บรรทัดที่ 2 กำหนด ng-app ชื่อว่า project ตัวก็สำหรับอ้างอิงใน angularjs
<html ng-app="project">
2. บรรทัดที่ 5 - 9 จะเป็นการโหลดไฟล์ js ที่จำเป็นต้องใช้ใน project นี้ จะเห็นว่ามีการเรียกใช้ไฟล์
มากกว่า ตัวอย่าง ในตอนที่ 2 ทั้งนี้ก็เพราะ จะมีการใช้งาน บางอย่างเพิ่มเข้ามา
โดย
บรรทัดที่ 5 ก็เรียกใช้งาน angularjs ปกติ
บรรทัดที่ 6 เรียกใช้งาน ngResource module ของ angularjs ไว้สำหรับใช้งาน การส่งค่าข้อมูล
คล้ายกับการใช้งาน ajax ส่งค่าและเรียกแสดงข้อมูล รู้แบบคร่าวๆ ก่อน
บรรทัดที่ 7 เรียกใช้งาน ngRoute module ของ angularjs ใช้สำหรับจัดเส้นทางการเรียกใช้ไฟล์
โดยจะใช้คู่กับ ng-view หมายถึง พอเรียกไฟล์มาแล้ว ก็ให้แสดง ในส่วนของ ng-view ที่เรากำหนด
<div ng-view></div>
<div ng-view> ตัวนี้จะเป็นส่วนที่จะแสดงข้อมูล จากการเรียกใช้งาน ngRoute module ของ angularjs
บรรทัดที่ 8-9 สองบรรทัดนี้ จะเป็นส่วนเรียกใช้งานภายนอก ให้เป็นไปตามเงื่อนไข ของการทดสอบ คือ
มีการ บันทึกข้อมูล แก้ไข และลบ ดังนั้น firebase ก็ให้เข้าใจว่า เป็น server สำหรับเก็บข้อมูล ในที่นี้
angularjs เขาเตรียมสำหรับให้เราทดสอบแล้ว ก็ใช้งานได้ แต่ถ้าเราอยาก ใช้งานเอง ก็สามารถสมัครฟรี
ได้ที่
บรรทัดที่ 10 เรียกใช้ไฟล์ project.js ตัวนี้ใช้สำหรับเขียนโค้ด angularjs ให้เราสร้างไฟล์ project.js ไว้
แล้วโค้ดจะอธิบายต่อด้านล่าง
บรรทัดที่ 11 โหลดไฟล์ css bootstrap อันนี้ก็สำหรับไว้จัดรูปแบบหน้าตา หรือก็คือตกแต่งการแสดง html
ให้สวยงาม โดยไม่ต้องเขียน css เอง สามารถเรียกใช้ตาม class ที่มีไว้แล้ว
บรรทัดที่ 20 จะมี ng-view ที่กำหนดในแท็ก div ตัวนี้ คือส่วนหรือตำแหน่ง ที่เราจะโหลดข้อมูลมาแสดง
โค้ดไฟล์ที่สอง ไฟล์ template ชื่อ list.html
<input type="text" ng-model="search" class="form-control " placeholder="Search"> <br> <table class="table table-striped"> <thead> <tr> <th>Project</th> <th>Description</th> <th style="text-align:center;"><a href="#/new"><i class="glyphicon glyphicon-plus"></i></a></th> </tr> </thead> <tbody> <tr ng-repeat="project in projects | filter:search | orderBy:'name'"> <td><a ng-href="{{project.site}}" target="_blank">{{project.name}}</a></td> <td>{{project.description}}</td> <td align="center"> <a ng-href="#/edit/{{project.$id}}"><i class="glyphicon glyphicon-pencil"></i></a> </td> </tr> </tbody> </table>
ไฟล์ template นี้ ก็จะเป็นรูปแบบ ของการแสดงข้อมูลแบบตาราง
การทำงานของโปรแกรมนี้ คือ เมื่อเปิดขึ้นมา ก็จะมีการไปดึงข้อมูลจาก
firebase.com (คำอธิบายจะแสดงในไฟล์ project.js) แล้วก็มาแสดงลงในตาราง
ตาม รูปแบบ template ไฟล์นี้ จะขออธิบาย ตามบรรทัด
class css ตัวไหน ไม่ได้อธิบาย ให้ถือวาเป็น css ของ bootstrap ใช้สำหรับจัดรูปแบบเท่านั้น
ไม่ได้เกี่ยวข้องกับ angularjs
บรรทัดที่ 1 ng-model เท่ากับ search ตัวนี้ คำกำหนดค่า ให้ input text สามารถ ค้นหาข้อมูลใน
รายการที่แสดงได้
<input type="text" ng-model="search" class="form-control " placeholder="Search">
บรรทัดที่ 12 ส่วนนี้จะมี directive ชื่อ ng-repeat ตามที่เราเข้าใจมาก่อนแล้ว คือ ใช้สำหรับวนลูปแสดงข้อมูลซ้ำ
<tr ng-repeat="project in projects | filter:search | orderBy:'name'">
project in projects ให้เข้าใจว่า จะใช้ชื่อ project เพื่ออ้างอิงข้อมูลใน projects
ที่เป็นลิสรายการ ที่ดึงมาจาก firebase.com
filter:search ให้เข้าใจว่า ข้อมูลที่แสดงในแถวในตารางนี้
สามารถค้นหาได้ จาก ng-model ที่ชื่อว่า search
orderBy:'name'" ให้เข้าใจว่า ลิสรายการที่วนลูปมาแสดง จะเรียงตาม ชื่อ a-z
โดย name คือ ชื่อของรายการ ที่อ้างอิงมาจาก projects.name
บรรทัดที่ 13-14 ก็จะเป็น template สำหรับแสดงข้อมูล
{{project.site}} แสดง url
{{project.name}} แสดงชื่อ
{{project.description}} แสดงคำอธิบาย
บรรทัดที่ 16 จะมี template ตรงลิ้งค์ {{project.$id}} สำหรับส่งค่าการแก้ไขข้อมูล
<a ng-href="#/edit/{{project.$id}}"><i class="glyphicon glyphicon-pencil"></i></a>
ng-href จะถูกใช้งานแทน href แบบธรรมดา เสียส่วนใหญ่ กรณีใช้งาน angularjs
โค้ดไฟล์ที่สาม ไฟล์ template ชื่อ detail.html
<form role="form" name="myForm"> <div class="control-group" ng-class="{'has-error': myForm.name.$invalid && !myForm.name.$pristine}"> <label class="control-label">Name</label> <input type="text" class="form-control" name="name" ng-model="project.name" required> <span ng-show="myForm.name.$error.required && !myForm.name.$pristine" class="text-danger"> Required {{myForm.name.$pristine}} </span> </div> <div class="control-group" ng-class="{'has-error': myForm.site.$invalid && !myForm.site.$pristine}"> <label class="control-label">Website</label> <input type="url" class="form-control" name="site" ng-model="project.site" required> <span ng-show="myForm.site.$error.required && !myForm.site.$pristine" class="text-danger"> Required</span> <span ng-show="myForm.site.$error.url" class="text-danger"> Not a URL</span> </div> <div class="control-group"> <label>Description</label> <textarea name="description" class="form-control" rows="3" ng-model="project.description"></textarea> </div> <br> <a href="#/" class="btn btn-default">Cancel</a> <button ng-click="save()" ng-disabled="myForm.$invalid" class="btn btn-primary">Save</button> <button ng-click="destroy()" ng-show="project.$id" class="btn btn-danger">Delete</button> </form>
ไฟล์ template นี้ ก็จะเป็นรูปแบบ สำหรับฟอร์ม สำหรับกรอกข้อมูล ในการเพิ่ม แก้ไย หรือลบ
การทำงาน เมื่อมีการ กดเพื่อเพิ่ม หรือกดเพื่อแก้ไข มาจากหน้า ลิสรายการ
ก็จะมาที่ไฟล์ detail.html
ย้ำอีกครั้ง ตาม รูปแบบ template ไฟล์นี้ จะขออธิบาย ตามบรรทัด
class css ตัวไหน ไม่ได้อธิบาย ให้ถือวาเป็น css ของ bootstrap ใช้สำหรับจัดรูปแบบเท่านั้น
ไม่ได้เกี่ยวข้องกับ angularjs
บรรทัดที่ 1 เราต้องกำหนดชื่อ ให้กับ form เพื่อใช้ในการอ้างอิง ในนี้กำหนดเป็น
<form role="form" name="myForm">
myForm จะเป็นชื่อที่ใช้สำหรับการอ้างอิง
บรรทัดที่ 2 เริ่มต้นรูปแบบการตรวจสอบและแสดงผล ฟอร์ม สังเกตจะมี ng-class ให้เข้าใจว่า เหมือน
การกำหนด css class ให้กับแท็กนั้นๆ
<div class="control-group" ng-class="{'has-error': myForm.name.$invalid && !myForm.name.$pristine}">
แท็ก div นี้จะมี css ของ bootstrap เดิมอยู่แล้วคือ control-group
ส่วน ng-class จะเป็นการกำหนด css class ในรูปแบบของ angularjs
โดย ng-class="" ในรูปแบบ css class ก็คือ class="ng-class: ;"
มาดูเงื่อนไขกัน myForm.name.$invalid
$invalid เป็นการตรวจสอบฟอร์ม ของ angularjs โดยมีค่าเป็น true ถ้าไม่มีการกำหนดค่าใดๆ
myForm.name.$invalid จึงหมายถึง
ถ้า input text ชื่อ name ของฟอร์ม myForm ไม่มีการกำหนดค่า ให้มีค่าเป็น true
เงื่อนไข !myForm.name.$pristine
สังเกตว่ามี ! ซึ่งหมายถึง ตรงข้าม เรามาดู ตรง myForm.name.$pristine ก่อน
$pristine เป็นการตรวจสอบว่ามีการ มีการกระทำอะไรกับสวนตรวจสอบนั้นๆหรือไม่ ถ้า
ผู้ใข้ไม่ได้พิมพ์ ไม่ได้กดอะไรในส่วนแท็กนั้น ก็จะมีค่าเป็น true ง่ายคือ ยังไม่ได้ทำอะไร เป็น true
myForm.name.$pristine จึงหมายถึง
ถ้าผู้ใช้ยังไม่ได้ทำอะไรกับ input text ชื่อ name ของฟอร์ม myForm ให้มีค่าเป็น true
แต่เนื่องจาก มี ! ซึ่งหมายถึง ค่าตรงข้าม
!myForm.name.$pristine ค่าจึงเป็น false
มาดูแบบเต็ม
myForm.name.$invalid && !myForm.name.$pristine
คือ ไม่มีการกำหนดค่าใน input text name และ (ตรงข้าม)ผู้ใช้ยังไม่ได้ทำอะไรกับ input type text
หรือ ก็คือ ไม่มีการกำหนดค่าใน input text name และผู้ใช้ ได้ทำอะไรกับ input type text แล้ว
สรูปคือ ถ้าไม่กรอกข้อมูล หรือข้อมูลเป็นค่าว่าง เงื่อนไข จะเป็น true
ng-class="{'has-error': myForm.name.$invalid && !myForm.name.$pristine}"
has-error คือ css class ของ bootstrap จะได้
ng-class="{'has-error': true}"
หมายถึง ให้ใช้ class has-error กับแท็ก div นี้ ถ้าเป็นไปตามเงื่อนไข
ซึ่งจะทำให้ เวลาเราไม่พิมพ์ข้อมูล ก็จะมีลักษณะการแจ้งเตือนเป็นข้อความสีแดง หรือ
การเน้น hilight input text ว่าจำเป็นต้องกรอกข้อมูล เป็นต้น
บรรทัดที่ 4 กำหนดค่าของ input text ด้วย ng-model="project.name" คือมีค่าเท่ากับ project.name
และมีการกำหนด required เพื่อให้มีการตรวจสอบ ว่า ช่องนี้จำเป็นต้องกรอก
บรรทัดที่ 5-7 เป็นส่วนของ การแจ้งเตือน เมื่อมีการตรวจสอบการกรอกข้อมูลเพิ่มเติม
ng-show ให้เข้าใจว่า ใช้แสดง แท็ก span แจ้งข้อความเตือน ตามเงื่อนไข
myForm.name.$error.required && !myForm.name.$pristine
myForm.name.$error.required เมื่อ ไม่กรอกข้อมูล
!myForm.name.$pristine เมื่อ ผู้ใช้ ได้ทำอะไรกับ input type text แล้ว
<span ng-show="myForm.name.$error.required && !myForm.name.$pristine" class="text-danger"> Required {{myForm.name.$pristine}} </span>
{{myForm.name.$pristine}} คือ template สำหรับแสดงค่า ผู้ใช้ ได้ทำอะไรกับ input type text แล้วหรือไม่
มี่ค่าเป็น true หรือ false
บรรทัดที่ 9-13 จะคล้ายกับ เนื้อหาด้านบนที่อธิบายแล้ว แค่เป็นจาก input text ชื่อ name เป็น site
บรรทัดที่ 14 เนื่องจาก input type ชื่อ site จะมีการตรวจสอบ ว่าเป็น รูปแบบ url จริงหรือไม่ จึงมีการ
กำหนดการตรวจสอบ การแสดงเพิ่มเติม ด้วย
ng-show="myForm.site.$error.url"
myForm.site.$error.url คือให้แสดงข้อความนี้ ถ้า url ที่กรอก มีรูปแบบไม่ถูกต้อง
บรรทัดที่ 19 ให้แสดงค่าและกำนหดค่า ใน textarea ด้วย project.description
โดยใช้ ng-model="project.description"
บรรทัดที่ 22 ปุ่มยกเลิก กดแล้วจะลิ้งค์ไปดึงลิสรากายมาแสดง การทำงานอยู่ในไฟล์ project.js
สังเกตการส่งค่า href="#/"
บรรทัดที่ 23 ปุ่มสำหรับกรณีแก้ไข หรือบันทึกข้อมูล ด้วยฟ้งก์ช่ัน save() ด้วย ng-click หรือก็คือเมื่อคลิก
นอกจากนั้นยังมี ng-disabled="myForm.$invalid"
myForm.$invalid ถ้าฟอร์มไม่พร้อมสำหรับ ส่งข้อมูล ng-disabled จะมีค่าเท่ากับ true
ให้เข้าใจว่า ปุ่มนี้จะเป็น disabled หรือไม่ทำงาน ถ้า ฟอร์มไม่พร้อมส่งข้อมูล
บรรทัดที่ 25-26 ปุ่มสำหรับกรณีลบ ด้วยการคลิก ng-click แล้วเรียกใช้ฟังก์ชั่น destroy()
โดยปุ่มลบ จะแสดงหรือไม่ขึ้นกับเงื่อนไข ng-show="project.$id"
คือ ถ้ามีการแก้ไข ก็จะมีการส่งค่า project.$id มา เมื่อมีค่า project.$id ก็จะทำให้ ng-show=true
หรือก็คือแสดงปุ่มให้ลบนั้นเอง
ยาวมากๆ สำหรับเนื้อหาในตอนนี้ ถึงส่วนสุดท้ายแล้ว
ส่วนสุดท้ายจะเป็นไฟล์ project.js คำอธิบายขอแสดงในไฟล์เลย
โค้ดไฟล์ที่สี่ ไฟล์สุดท้าย project.js สำหรับจัดการทั้งหมด
คำอธิบายแสดงในโค้ด
angular.module('project', ['ngRoute', 'firebase']) // กำหนดค่า เพื่อเรียกใช้งาน firebase .value('fbURL', 'https://angularjs-projects.firebaseio.com/') // เชื่อมต่อกับ firebase.com เพื่อรับส่งข้อมูลกลับมา ด้วยการใช้งาน angularfire library .factory('Projects', function($firebase, fbURL) { คืนค่ากลับมาไว้ใน Projects return $firebase(new Firebase(fbURL)).$asArray(); // คืนค่ามาในรูปแบบของ object array }) // ตรวจสอบและกำหนดการแสดงข้อมูล ด้วย route module ของ angularjs .config(function($routeProvider) { $routeProvider .when('/', { // เมื่อมีลิ้งค์ไปที่ตำแหน่งนี / controller:'ListCtrl', // กำหนด controller เป็น ListCtrl templateUrl:'list.html' // โดยเรียกใช้ template จากไฟล์ list.html }) .when('/edit/:projectId', { // เมื่อลิ้งค์ไปยังรายการแก้ไข โดยส่งค่า projectId ไปด้วย controller:'EditCtrl', // กำหนด controller เป็น EditCtrl templateUrl:'detail.html' // โดยเรียกใช้ template จากไฟล์ detail.html }) .when('/new', {// เมื่อลิ้งค์ไปหน้าสำหรับเพิ่มข้อมูลใหม่ controller:'CreateCtrl', // กำหนด controller เป็น CreateCtrl templateUrl:'detail.html' // โดยเรียกใช้ template จากไฟล์ detail.html }) .otherwise({ redirectTo:'/' // ให้ไปที่หน้าลิสแสดงรายการ }); }) // เมื่อมีการใช้งานในส่วนของการแสดงลิสรายการ .controller('ListCtrl', function($scope, Projects) { // ส่งค่าข้อมูล Projects ที่ได้จากการเรียกใช้งาน firebase ไปแสดง $scope.projects = Projects; // แสดงข้อมูลใน ng-view }) // เมื่อมาในส่วนของการเพิ่มข้อมูลใหม่ .controller('CreateCtrl', function($scope, $location, $timeout, Projects) { // ส่งค่าต่างๆ ที่จำเป็นเข้ามา $scope.save = function() { // ส่วนนี้เป็นการใช้งาน คำสั่ง $add ของ firebase สำหรับ angularjs // ให้มีการส่ง ค่าจาก $scope.project ไปบันทึก Projects.$add($scope.project).then(function(data) { // หลังจากบันทึกข้อมูลเรียบร้อยให้ไปที่หน้าแสดง รายการ $location.path('/'); }); }; }) // เมื่อมาที่ส่วนของฟอร์มสำหรับการแก้ไข .controller('EditCtrl', function($scope, $location, $routeParams, Projects) { // ส่งค่าต่างๆ ที่จำเป็นเข้ามา var projectId = $routeParams.projectId, // กำหนด id สำหรับ อ้างอิง projectIndex; // กำหนดตัวแปรสำหรับ อ้างอิง $scope.projects = Projects; // กำหนดชุดข้อมูลจาก firebase ทีได้ไว้ใน $scope.projects projectIndex = $scope.projects.$indexFor(projectId); // เก็บค่า index อ้างอิง $scope.project = $scope.projects[projectIndex]; // ชุดข้อมูลของ ฟอร์ม // เมื่อทำการลบข้อมูล $scope.destroy = function() { // ส่วนนี้เป็นการใช้งาน คำสั่ง $remove ของ firebase สำหรับ angularjs // ให้มีการส่ง ค่าจาก $scope.project ไปทำการลบข้อมูล $scope.projects.$remove($scope.project).then(function(data) { // หลังจากลบข้อมูลเรียบร้อยให้ไปที่หน้าแสดง $location.path('/'); }); }; // เมื่อบันทีกข้อมูลกรณีแก้ไข $scope.save = function() { // ส่วนนี้เป็นการใช้งาน คำสั่ง $save ของ firebase สำหรับ angularjs // ให้มีการส่ง ค่าจาก $scope.project ไปบันทึก $scope.projects.$save($scope.project).then(function(data) { // หลังจากบันทึกข้อมูลเรียบร้อยให้ไปที่หน้าแสดง รายการ $location.path('/'); // คล้ายๆ คำสั่ง window.location }); }; });
โค้ดไฟล์ที่สี่ ไฟล์สุดท้าย project.js ไม่มีคำอธิบาย
angular.module('project', ['ngRoute', 'firebase']) .value('fbURL', 'https://angularjs-projects.firebaseio.com/') .factory('Projects', function($firebase, fbURL) { return $firebase(new Firebase(fbURL)).$asArray(); }) .config(function($routeProvider) { $routeProvider .when('/', { controller:'ListCtrl', templateUrl:'list.html' }) .when('/edit/:projectId', { controller:'EditCtrl', templateUrl:'detail.html' }) .when('/new', { controller:'CreateCtrl', templateUrl:'detail.html' }) .otherwise({ redirectTo:'/' }); }) .controller('ListCtrl', function($scope, Projects) { $scope.projects = Projects; }) .controller('CreateCtrl', function($scope, $location, $timeout, Projects) { $scope.save = function() { Projects.$add($scope.project).then(function(data) { $location.path('/'); }); }; }) .controller('EditCtrl', function($scope, $location, $routeParams, Projects) { var projectId = $routeParams.projectId, projectIndex; $scope.projects = Projects; projectIndex = $scope.projects.$indexFor(projectId); $scope.project = $scope.projects[projectIndex]; $scope.destroy = function() { $scope.projects.$remove($scope.project).then(function(data) { $location.path('/'); }); }; $scope.save = function() { $scope.projects.$save($scope.project).then(function(data) { $location.path('/'); }); }; });
ตัวอย่าง
เนื้อหาในตอนที่ 3 จะค่อนข้างซับซ้อบ อ่านรอบเดียวอาจจะไม่เข้าใจ
อาจจำเป็นต้องดูหลายๆ ครั้ง และเพื่อให้เข้าใจการทำงานมากข้ึน ให้ลอง
ทดสอบการทำงานของตัวโปรแกรม จากตัวอย่างด้านบน โดยเพิ่มลบ แก้ไข
ค้น ข้อมูลได้ตามต้องการ แล้วมาเทียบ กับการทำงานของโค้ดคำสั่งในแต่ละส่วน
สำหรับเนื้อหาในตอนต่อไป จะมีอะไร รอติดตาม