ไดเร็กทีฟแบบกำหนดเองของ AngularJS: เทมเพลต ขอบเขต และการแทรกข้อมูล
⚡ สรุปอย่างชาญฉลาด
ไดเร็กทีฟแบบกำหนดเองใน AngularJS ขยาย HTML ด้วยพฤติกรรมที่นำกลับมาใช้ซ้ำได้ซึ่งคุณกำหนดเอง โรงงานสร้างไดเร็กทีฟจะส่งคืนอ็อบเจ็กต์คำจำกัดความ และ AngularJS จะแนบพฤติกรรมนั้นไปยังตำแหน่งใดก็ตามที่องค์ประกอบ แอตทริบิวต์ คลาส หรือความคิดเห็นที่ตรงกันปรากฏในมาร์กอัป

Custom Directive ใน AngularJS คืออะไร?
A คำสั่งที่กำหนดเอง ใน AngularJS `directive` ที่ผู้ใช้กำหนดเองจะขยาย HTML ด้วยพฤติกรรมที่คุณเขียนขึ้นเอง โดยจะถูกลงทะเบียนไว้ใน `<directive>` โมดูล โดยใช้ฟังก์ชัน "directive" และ AngularJS จะแนบฟังก์ชันนี้ไปยังตำแหน่งที่องค์ประกอบหรือแอตทริบิวต์ที่ตรงกันปรากฏในหน้าเว็บ แม้ว่า AngularJS จะมีประสิทธิภาพมากมายก็ตาม คำสั่งในตัว นอกกรอบ เช่น ng-ซ้ำบางครั้งอาจจำเป็นต้องใช้คำสั่งเฉพาะเพิ่มเติม
⚠️ การแก้ไข: ข้อความเดิมระบุว่าคำสั่งแบบกำหนดเองจะ "แทนที่องค์ประกอบที่ใช้คำสั่งนั้น" แต่โดยค่าเริ่มต้นแล้วจะไม่เป็นเช่นนั้น: template ถูกแทรก ภายใน องค์ประกอบที่ตรงกัน จำเป็นต้องเปลี่ยน replace: trueเลิกใช้แล้วตั้งแต่ AngularJS เวอร์ชัน 1.3
⚠️ ประกาศแจ้งการสนับสนุน: AngularJS มาถึงแล้ว สิ้นสุดชีวิตเมื่อวันที่ 31 ธันวาคม 2021 และจะไม่ได้รับการแก้ไขข้อบกพร่องหรือการแก้ไขด้านความปลอดภัยเพิ่มเติมอีกต่อไป ทุกสิ่งด้านล่างนี้ยังคงถูกต้องสำหรับการบำรุงรักษาโค้ด AngularJS 1.x ที่มีอยู่ ใน Angular รุ่นใหม่ สิ่งที่เทียบเท่ากันคือคอมโพเนนต์และไดเร็กทีฟแอตทริบิวต์
เหตุผลที่ควรใช้ directive แบบกำหนดเองก็คือเรื่องการทำซ้ำ เมื่อโค้ด HTML หรือโครงสร้าง DOM เดียวกันปรากฏบนหลายหน้าจอ directive จะเปลี่ยนโค้ดนั้นให้กลายเป็นแท็กที่มีชื่อเพียงแท็กเดียว
จะสร้างคำสั่งที่กำหนดเองได้อย่างไร?
เรามาดูตัวอย่างวิธีการสร้างไดเร็กทีฟแบบกำหนดเองใน AngularJS กัน
ในกรณีของเรา คำสั่งกำหนดเองนี้จะแทรกแท็ก div ที่มีข้อความ "Angular JS Tutorial" ลงในหน้าเว็บของเราเมื่อมีการเรียกใช้คำสั่งนั้น
ภาพหน้าจอที่มีคำอธิบายประกอบด้านล่างนี้ แสดงหมายเลขของส่วนประกอบทั้งสี่ส่วนที่อธิบายไว้หลังจากนั้น
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { template: '<div>Angular JS Tutorial</div>' } }); </script> </body> </html>
⚠️ คำเตือน — ข้อบกพร่องต่างๆ จะถูกเก็บรักษาไว้ในรายการนี้: โค้ดถูกคัดลอกมาตรงตามที่เผยแพร่ทุกประการ มีข้อผิดพลาดสามประการที่เกิดขึ้นซ้ำๆ ในทุกรายการที่แสดงรายการนี้: chrset="UTF 8" ควรจะเป็น charset="UTF-8"; angular.js และ angular.min.js ทั้งสองส่วนโหลดพร้อมกัน ดังนั้น AngularJS จึงโหลดสองครั้ง และ angular-route.js โหลดก่อนไฟล์ที่มันขึ้นอยู่ด้วย
Code คำอธิบาย
- เรากำลังสร้าง a โมดูล สำหรับแอปพลิเคชัน AngularJS ของเรา จำเป็นต้องสร้าง directive แบบกำหนดเอง เนื่องจาก directive นั้นจะถูกสร้างขึ้นโดยใช้โมดูลนี้
- ขณะนี้เรากำลังสร้างไดเร็กทีฟแบบกำหนดเองชื่อ “ng”Guru“และกำหนดฟังก์ชันโรงงานที่จะเก็บโค้ดที่กำหนดเองสำหรับไดเร็กทีฟของเรา”
- เราใช้พารามิเตอร์เทมเพลต ซึ่งเป็นพารามิเตอร์ที่ AngularJS กำหนดไว้สำหรับไดเร็กทีฟแบบกำหนดเอง ในที่นี้ เรากำหนดว่าเมื่อใดก็ตามที่ใช้ไดเร็กทีฟนี้ ให้ใช้ค่าของเทมเพลตและแทรกเข้าไปในโค้ดที่เรียกใช้
- ในขั้นตอนนี้ เราจะใช้คำสั่ง “ng-guru” ที่เราสร้างขึ้นเอง เมื่อเราทำเช่นนี้ ค่าที่เรากำหนดไว้สำหรับเทมเพลตของเรา...
<div>Angular JS Tutorial</div>จะทำการฉีดเข้าที่นี่ในตอนนี้
การตั้งชื่อและการทำให้เป็นมาตรฐานของคำสั่ง
ชื่อไม่ตรงกันระหว่าง Javaสคริปต์และ HTML ทำให้ผู้เริ่มต้นส่วนใหญ่สับสน AngularJS ทำให้เป็นปกติ ทุกองค์ประกอบและชื่อแอตทริบิวต์ก่อนที่จะจับคู่: มันจะตัดทุกอย่างออก x- or data- คำนำหน้า จากนั้นแปลงชื่อที่คั่นด้วย -, : or _ แปลงเป็นรูปแบบ camelCase นั่นคือเหตุผล ngGuru ที่ตรงกัน ng-guru, และทำไม data-ng-guru ก็ใช้ได้เช่นกัน รูปแบบ camelCase คือชื่อที่ถูกต้อง ส่วน kebab-case เป็นเพียงวิธีการเขียนใน HTML เท่านั้น
⚠️ การแก้ไข: ข้อความต้นฉบับระบุว่า ชื่อคำสั่งแบบกำหนดเอง “ควรขึ้นต้นด้วยตัวอักษร 'ng'” เอกสารอย่างเป็นทางการของ AngularJS แนะนำตรงกันข้าม: อย่าใส่คำนำหน้าคำสั่งของคุณเองด้วย ngหรืออาจเกิดการขัดแย้งกับฟังก์ชันในตัวในอนาคต ให้ใช้คำนำหน้าของคุณเอง เช่น g99Guru เขียนเป็น g99-guru.
หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้
Output:
ผลลัพธ์แสดงให้เห็นอย่างชัดเจนว่า directive ng-guru แบบกำหนดเองของเรา ซึ่งมีเทมเพลตที่กำหนดไว้สำหรับการแสดงข้อความแบบกำหนดเองนั้น แสดงผลในเบราว์เซอร์แล้ว ขั้นตอนต่อไปคือการใส่สิ่งที่คุ้มค่าแก่การนำกลับมาใช้ใหม่ลงในเทมเพลตนั้น
วิธีการสร้างคำสั่งที่สามารถนำกลับมาใช้ใหม่ได้
เราได้เห็นพลังของคำสั่งแบบกำหนดเองแล้ว แต่เราสามารถนำมันไปสู่อีกระดับได้ด้วยการสร้างคำสั่งที่นำมาใช้ซ้ำได้ของเราเอง
สมมติว่าเราต้องการแทรกโค้ดที่จะแสดงแท็ก HTML ด้านล่างนี้บนหน้าจอหลายๆ จอ ซึ่งโดยพื้นฐานแล้วก็คือช่องสำหรับป้อน "ชื่อ" และ "อายุ" ของผู้ใช้
เพื่อนำส่วนประกอบนี้ไปใช้ซ้ำบนหน้าจอหลายๆ จอโดยไม่ต้องเขียนโค้ดใหม่ทุกครั้ง เราจึงสร้างตัวควบคุมหลัก หรือไดเร็กทีฟ ใน AngularJS เพื่อรวบรวมตัวควบคุมทั้งสองนี้ไว้ด้วยกัน
ดังนั้น ตอนนี้แทนที่จะเขียนโค้ดนั้นซ้ำในทุกหน้าจอ เราจึงฝังมันไว้ใน directive แล้วเรียกใช้ directive นั้นแทน ภาพหน้าจอด้านล่างแสดงบรรทัดเดียวที่เปลี่ยนแปลงไป
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { template: ' Name <input type="text"><br><br> Age<input type="text">' }; }); </script> </body> </html>
Code คำอธิบาย
- ในโค้ดตัวอย่างสำหรับ directive แบบกำหนดเอง สิ่งที่เปลี่ยนแปลงคือค่าที่ส่งให้กับพารามิเตอร์ template เท่านั้น แทนที่จะใช้แท็กธรรมดาหรือข้อความบรรทัดเดียว เราป้อนส่วนของช่องป้อนข้อมูลสองช่องสำหรับ "ชื่อ" และ "อายุ" ที่ต้องแสดงบนหน้าเว็บของเรา
💡 เคล็ดลับ: เทมเพลตที่ยาวขนาดนี้ควรอยู่ในไฟล์แยกต่างหาก สลับ template สำหรับ templateUrl: 'name-age.html'.
หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้
Output:
จากผลลัพธ์ข้างต้น เราจะเห็นว่าโค้ดส่วนหนึ่งจากเทมเพลตของไดเร็กทีฟแบบกำหนดเองถูกเพิ่มเข้าไปในหน้าเว็บแล้ว เทมเพลตยังคงเป็นแบบคงที่ ดังนั้นขั้นตอนต่อไปคือการป้อนข้อมูลเข้าไปในเทมเพลต
ไดเร็กทีฟและสโคปของ AngularJS
การขอ ขอบเขต หมายถึง ตัวเชื่อมที่ยึดตัวควบคุม (Controller) กับมุมมอง (View) โดยทำหน้าที่จัดการข้อมูลระหว่างมุมมองและตัวควบคุม
เมื่อสร้างไดเร็กทีฟ AngularJS แบบกำหนดเอง โดยค่าเริ่มต้น ไดเร็กทีฟเหล่านั้นจะสามารถเข้าถึงอ็อบเจ็กต์ขอบเขต (scope object) ในคอนโทรลเลอร์หลักได้ ซึ่งเป็นอ็อบเจ็กต์ขอบเขตเดียวกัน ไม่ใช่สำเนา
ด้วยวิธีนี้ จึงกลายเป็นเรื่องง่ายสำหรับคำสั่งแบบกำหนดเองในการใช้ประโยชน์จากข้อมูลที่ส่งผ่านไปยังตัวควบคุมหลัก
เรามาดูตัวอย่างวิธีการใช้ขอบเขตของคอนโทรลเลอร์หลักในไดเร็กทีฟที่กำหนดเองของเรากัน ภาพหน้าจอด้านล่างแสดงขั้นตอนทั้งสี่ขั้นตอน
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp" ng-controller="DemoController"> <div ng-guru=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.controller('DemoController',function($scope) { $scope.tutorialName = "Angular JS"; }); app.directive('ngGuru',function(){ return { template: '<div>{{tutorialName}}</div>' } }); </script> </body> </html>
Code คำอธิบาย
- ขั้นแรก เราสร้างคอนโทรลเลอร์ชื่อ “DemoController” ในคอนโทรลเลอร์นี้ เรากำหนดตัวแปรชื่อ tutorialName และเชื่อมโยงตัวแปรนี้เข้ากับอ็อบเจ็กต์ขอบเขต (scope object) ด้วยคำสั่งเดียว:
$scope.tutorialName = "Angular JS". - ในคำสั่งที่กำหนดเองของเรา เราสามารถเรียกตัวแปร “tutorialName” ได้โดยใช้นิพจน์ ตัวแปรนี้จะสามารถเข้าถึงได้เนื่องจากมีการกำหนดไว้ในคอนโทรลเลอร์ “DemoController” ซึ่งจะกลายเป็นพาเรนต์สำหรับคำสั่งนี้
- เราอ้างอิงตัวควบคุมในแท็ก div ซึ่งจะทำหน้าที่เป็นแท็ก div หลักของเรา โปรดทราบว่าจะต้องดำเนินการนี้ก่อนเพื่อให้คำสั่งที่กำหนดเองของเราเข้าถึงตัวแปร TutorialName
- ในที่สุดเราก็แนบคำสั่งที่กำหนดเอง “ng-guru” เข้ากับแท็ก div ของเรา
หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้
Output:
ผลลัพธ์ข้างต้นแสดงให้เห็นอย่างชัดเจนว่า directive ที่กำหนดเองของเรา “ng-guru” ใช้ตัวแปร scope ชื่อ tutorialName ในคอนโทรลเลอร์หลัก การแชร์ scope กับคอนโทรลเลอร์หลักนั้นสะดวก แต่ไม่ใช่สิ่งที่คุณต้องการเสมอไป
การใช้คอนโทรลเลอร์พร้อมคำสั่ง
AngularJS มอบความสามารถดังต่อไปนี้ เข้าถึงตัวแปรสมาชิกของคอนโทรลเลอร์ได้โดยตรงจากคำสั่งที่กำหนดเอง โดยไม่จำเป็นต้องใช้วัตถุขอบเขต
สิ่งนี้มีความจำเป็นในบางครั้ง เนื่องจากแอปพลิเคชันอาจมีอ็อบเจ็กต์ขอบเขตหลายตัวที่อยู่ในคอนโทรลเลอร์หลายตัว ดังนั้นจึงมีโอกาสสูงที่จะเข้าถึงอ็อบเจ็กต์ขอบเขตของคอนโทรลเลอร์ที่ไม่ถูกต้องโดยไม่ได้ตั้งใจ
ในสถานการณ์เช่นนี้ มีวิธีที่จะระบุอย่างเจาะจงว่า “ฉันต้องการเข้าถึงคอนโทรลเลอร์นี้” จากคำสั่งของฉันได้
เรามาดูตัวอย่างกันว่าเราจะบรรลุเป้าหมายนี้ได้อย่างไร ภาพหน้าจอแสดงขั้นตอนทั้งสี่ขั้นตอนพร้อมหมายเลขกำกับไว้
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp" ng-controller="DemoController"> <div ng-guru99=""></div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.controller('DemoController',function() { this.tutorialName = "Angular"; }); app.directive('ngGuru99',function(){ return { controller: 'DemoController', controllerAs: 'ctrl', template: '{{ctrl.tutorialName}}' }; }); </script> </body> </html>
Code คำอธิบาย
- ขั้นแรก เราสร้างคอนโทรลเลอร์ชื่อ “DemoController” ในคอนโทรลเลอร์นี้ เราจะกำหนดตัวแปรชื่อ “tutorialName” และคราวนี้ แทนที่จะแนบตัวแปรนี้เข้ากับอ็อบเจ็กต์ขอบเขต เราจะแนบตัวแปรนี้เข้ากับคอนโทรลเลอร์โดยตรง
- ในคำสั่งที่กำหนดเองของเรา เราได้กล่าวถึงเป็นพิเศษว่าเราต้องการใช้คอนโทรลเลอร์ “DemoController” โดยใช้คีย์เวิร์ดพารามิเตอร์คอนโทรลเลอร์
- เราสร้างการอ้างอิงถึงคอนโทรลเลอร์โดยใช้พารามิเตอร์ “controllerAs” ซึ่งเป็นพารามิเตอร์ที่กำหนดโดย AngularJS และเป็นวิธีในการอ้างอิงคอนโทรลเลอร์โดยใช้ชื่อแทน
- สุดท้ายนี้ ในเทมเพลตของเรา เราใช้ข้อมูลอ้างอิงที่สร้างขึ้นในขั้นตอนที่ 3 และใช้ตัวแปรสมาชิกที่เชื่อมต่อโดยตรงกับตัวควบคุมในขั้นตอนที่ 1
หมายเหตุ สามารถเข้าถึงคอนโทรลเลอร์หลายตัวในไดเร็กทีฟเดียวได้โดยการระบุบล็อกของคอนโทรลเลอร์นั้นๆ คำสั่ง controllerAs และ template เพื่อเข้าถึง... ผู้ปกครอง แทนที่จะใช้ตัวควบคุมของคำสั่ง ให้ใช้ตัวควบคุมของคำสั่งแทน require.
หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้
Output:
ผลลัพธ์แสดงให้เห็นอย่างชัดเจนว่า directive ที่กำหนดเองนั้นเข้าถึง DemoController และตัวแปรสมาชิก tutorialName ที่เชื่อมโยงอยู่ และแสดงข้อความ “Angular” ทั้งสองวิธียังคงอ่านจาก shared scope ซึ่ง isolate scope ป้องกันไว้
ความแตกต่างระหว่างการผูกคำสั่ง @, =, & และ < ในขอบเขต Isolate Scope
คำสั่งที่ใช้ขอบเขตเดียวกันกับคำสั่งหลัก สามารถใช้ได้เพียงครั้งเดียวต่อคอนโทรลเลอร์เท่านั้น เพราะหากมีสองสำเนา จะทำให้เกิดการแย่งใช้ตัวแปรเดียวกัน scope วัตถุสร้าง ขอบเขตการแยก: คำสั่งนี้จะไม่เห็นข้อมูลภายนอกใดๆ นอกเหนือจากคุณสมบัติที่คุณระบุไว้ แต่ละรายการจะใช้สัญลักษณ์นำหน้าเพื่อประกาศวิธีการส่งค่าของรายการนั้นๆ
| สัญลักษณ์ | ทิศทาง | มูลค่าที่ได้รับ | ใช้สำหรับ |
|---|---|---|---|
| @ | ทางเข้าทางเดียว | คุณลักษณะในรูปแบบสตริงที่แทรกค่าเข้าไป | ป้ายกำกับ ชื่อเรื่อง ข้อความธรรมดาใดๆ |
| = | สองทาง | วัตถุหรือค่าที่มีชีวิตนั้นเอง | แบบจำลองคำสั่งที่เขียนกลับไป |
| & | การเรียกออกภายนอก | ฟังก์ชันที่ประเมินนิพจน์ในขอบเขตภายนอก | ฟังก์ชันเรียกกลับ เช่น เมื่อปิดแอป หรือเมื่อบันทึกแอป |
| < | ทางเข้าทางเดียว | มูลค่าที่ประเมินไว้ ไม่เคยเขียนเพิ่มขึ้น | ข้อมูลแบบอ่านอย่างเดียว เพิ่มเข้ามาใน AngularJS เวอร์ชัน 1.5 |
💡 เคล็ดลับ: เข้าถึงเพื่อ < ก่อน =การผูกข้อมูลแบบทางเดียว (One-way binding) นั้นมีต้นทุนในการตรวจสอบต่ำกว่า และช่วยป้องกันไม่ให้คอมโพเนนต์เปลี่ยนแปลงข้อมูลของผู้เรียกใช้งานโดยไม่แจ้งให้ทราบล่วงหน้า ส่วนถัดไปจะใช้... @.
คำสั่งและส่วนประกอบของ AngularJS: ng-transclude
ดังที่เราได้กล่าวไปแล้ว AngularJS มีจุดประสงค์เพื่อขยายฟังก์ชันการทำงานของ HTML- และเราได้เห็นแล้วว่าเราสามารถฉีดโค้ดได้อย่างไรโดยใช้คำสั่งที่นำมาใช้ซ้ำแบบกำหนดเองได้
แต่ในการพัฒนาแอปพลิเคชันบนเว็บสมัยใหม่ ยังมีแนวคิดเรื่องการพัฒนาอีกด้วยping เว็บคอมโพเนนต์ ซึ่งโดยพื้นฐานแล้วหมายถึงการสร้างแท็ก HTML ของเราเองที่สามารถนำมาใช้เป็นคอมโพเนนต์ในโค้ดของเราได้
ดังนั้น AngularJS จึงมอบพลังอีกระดับหนึ่งสำหรับการขยายแท็ก HTML โดยให้ความสามารถในการห่อหุ้มมาร์กอัปใดๆ ก็ตามที่ผู้เรียกใส่ไว้ภายในแท็ก
นี้จะกระทำโดย “ng-รวมคำสั่ง "ซึ่งบอกให้ AngularJS จับทุกอย่างที่อยู่ภายในแท็กที่กำหนดเองในมาร์กอัป และนำไปใส่ในเทมเพลต ณ จุดนั้น"
เรามาดูตัวอย่างวิธีการบรรลุเป้าหมายนี้กัน ภาพหน้าจอแสดงหมายเลขห้าประเด็นต่อไปนี้
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <pane title="{{title}}">Angular JS</pane> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('pane',function(){ return { transclude:true, scope :{title:'@'}, template: '<div style="border: 1px solid black;"> '+ '<ng-transclude></ng-transclude>'+ '</div>' }; }); </script> </body> </html>
Code คำอธิบาย
- เราใช้คำสั่งเพื่อกำหนดแท็ก HTML แบบกำหนดเองชื่อ “pane” โดยใช้ฟังก์ชันโรงงาน (factory function) ในการป้อนโค้ด ในผลลัพธ์ แท็ก pane จะแสดงข้อความ “Angular JS” ในกรอบสี่เหลี่ยมที่มีเส้นขอบสีดำทึบ
- คุณสมบัติ “transclude” ต้องตั้งค่าเป็น true ซึ่งเป็นการบอกให้ AngularJS เก็บมาร์กอัปภายในเดิมไว้และทำให้สามารถใช้งานได้
ng-transclude. - ในออบเจ็กต์ขอบเขต เรากำลังประกาศการผูกชื่อเรื่อง โดยปกติแล้วแอตทริบิวต์จะเขียนในรูปแบบคู่ชื่อ/ค่า เช่น
name="value"ในกรณีของเรา แอตทริบิวต์บนแท็ก pane คือ "title" และสัญลักษณ์ "@" สั่งให้ AngularJS คัดลอกแอตทริบิวต์นั้นมาเป็นสตริงที่แทรกเข้าไป - เทมเพลตจะวาดเส้นขอบสีดำทึบให้กับส่วนควบคุมของเรา และทำเครื่องหมายตำแหน่งที่จะแทรกมาร์กอัปที่แทรกเข้ามา
- สุดท้ายนี้ เราจะเรียกใช้แท็ก HTML ที่กำหนดเอง พร้อมกับแอตทริบิวต์ title ที่ได้กำหนดไว้
⚠️ การแก้ไข: ข้อความต้นฉบับระบุว่า “@” คือ “ข้อกำหนดจาก Angular” ไม่ใช่เพียงแค่พิธีการ: @ เป็นหนึ่งในสี่สัญลักษณ์การผูกขอบเขตแบบแยกส่วนในตารางด้านบน และจะส่งคืนค่าเป็นสตริงเสมอ เทมเพลตนี้จะไม่แสดงผลเช่นกัน {{title}}และไม่มีตัวควบคุมใดกำหนด titleดังนั้นจึงมองเห็นเฉพาะข้อความที่ถูกแทรกเข้าไปเท่านั้น
หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้
Output:
- ผลลัพธ์แสดงให้เห็นองค์ประกอบ pane ที่กำหนดเองซึ่งแสดงผลเป็นกล่องที่มีเส้นขอบและบรรจุข้อความที่แทรกเข้ามาว่า “Angular JS”
คำสั่งหนึ่งอาจประกอบด้วยคำสั่งย่อยอื่นๆ ซึ่งเป็นวิธีการประกอบชิ้นส่วนขนาดใหญ่เข้าด้วยกัน
คำสั่งย่อยซ้อนกัน
ใน AngularJS คำสั่ง (Directives) สามารถซ้อนกันได้ เช่นเดียวกับโมดูลภายในหรือฟังก์ชันภายในใดๆ ภาษาโปรแกรมคุณอาจต้องฝังคำสั่งไว้ภายในกันและกัน
คุณสามารถเข้าใจสิ่งนี้ได้ดีขึ้นโดยดูตัวอย่างด้านล่าง
ในตัวอย่างนี้ เรากำลังสร้าง 2 คำสั่งที่เรียกว่า "outer" และ "inner"
- คำสั่งภายในจะแสดงข้อความที่เรียกว่า "ภายใน"
- ในขณะที่คำสั่งภายนอกทำการเรียกคำสั่งภายในเพื่อแสดงข้อความที่เรียกว่า "ภายใน"
ภาพหน้าจอด้านล่างแสดงคำจำกัดความของคำสั่งทั้งสอง และจุดที่คำสั่งหนึ่งเรียกใช้คำสั่งอีกคำสั่งหนึ่ง
</head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <outer></outer> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('outer',function(){ return { restrict:'E', template: '<div><h1>Outer</h1><inner></inner></div>', }}); app.directive('inner',function(){ return { restrict:'E', template: '<div><h1>Inner</h1></div>', } }); </script> </body> </html>
⚠️ คำเตือน — ข้อบกพร่องนี้ยังคงอยู่ในรายการนี้: รายชื่อที่เผยแพร่เริ่มต้นที่ </head>พลาดการเปิดตัว <!DOCTYPE html>, <html> และ <head> เพิ่มบรรทัดเหล่านั้นก่อนเรียกใช้โปรแกรม
Code คำอธิบาย
- เรากำลังสร้างคำสั่งที่เรียกว่า "outer" ซึ่งจะทำหน้าที่เป็นคำสั่งหลักของเรา คำสั่งนี้จะทำการเรียกคำสั่ง "ภายใน"
- การขอ
restrict:'E'ตัวเลือกนี้บอกให้ AngularJS จับคู่แต่ละไดเร็กทีฟโดย ธาตุ ชื่อ ซึ่งเป็นเหตุผลที่สามารถเขียนเป็นแท็กได้<outer>และ<inner>ตัวอักษร 'E' เป็นตัวย่อของคำว่า 'Element' - ที่นี่เรากำลังสร้างคำสั่งภายในซึ่งแสดงข้อความ “ภายใน” ในแท็ก div
- ในเทมเพลตสำหรับไดเร็กทีฟภายนอก เรากำลังเรียกใช้ไดเร็กทีฟภายใน ดังนั้นตรงนี้เราจึงกำลังแทรกเทมเพลตจากไดเร็กทีฟภายในเข้าไปในไดเร็กทีฟภายนอก
- สุดท้ายนี้ เรากำลังเรียกคำสั่งภายนอกโดยตรง
⚠️ การแก้ไข: ข้อความต้นฉบับอ้างว่า restrict:'E' จำเป็นต้องใช้ "เพื่อให้แน่ใจว่าข้อมูลจากคำสั่งภายในพร้อมใช้งานสำหรับคำสั่งภายนอก" แต่ในความเป็นจริงแล้วมันไม่ได้ทำเช่นนั้น มันแค่ควบคุมเท่านั้น อย่างไร สามารถเขียนคำสั่งได้ การซ้อนกันทำงานได้เพราะคอมไพเลอร์จะเดินเข้าไปในเทมเพลตที่เพิ่งแทรกเข้าไปเรื่อยๆ ในการแบ่งปันข้อมูล ให้ใช้ require และ controller.
หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้
Output:
จากผลลัพธ์ที่ได้
- จะเห็นได้ว่าทั้งคำสั่งภายนอกและภายในถูกเรียกใช้งาน และข้อความในแท็ก div ทั้งสองแสดงผลออกมาแล้ว
การแสดงผลมาร์กอัปเป็นเพียงครึ่งหนึ่งของสิ่งที่ไดเร็กทีฟทำ อีกครึ่งหนึ่งคือการตอบสนองต่อผู้ใช้
การจัดการเหตุการณ์ในคำสั่ง
อีเวนต์ การกระทำต่างๆ เช่น การคลิกเมาส์หรือการคลิกปุ่ม สามารถจัดการได้จากภายในไดเร็กทีฟเอง โดยใช้ฟังก์ชันลิงก์ ฟังก์ชันลิงก์นี้เองที่ทำให้ไดเร็กทีฟสามารถเชื่อมโยงตัวเองกับองค์ประกอบ DOM ในหน้า HTML ได้
ไวยากรณ์:
ไวยากรณ์ของฟังก์ชันลิงก์แสดงไว้ด้านล่างนี้
link: function ($scope, element, attrs)
ฟังก์ชันลิงก์โดยปกติจะรับพารามิเตอร์ 3 ตัว ได้แก่ ขอบเขต องค์ประกอบที่คำสั่งเชื่อมโยงด้วย และแอตทริบิวต์ขององค์ประกอบเป้าหมาย อีกสองตัวคือตัวควบคุมที่จำเป็นและฟังก์ชันการแทรกข้อมูล จะถูกส่งมาเมื่อมีการใช้งาน
ขั้นตอนการคอมไพล์เทียบกับขั้นตอนการเชื่อมโยง
AngularJS ประมวลผล directive สองรอบ และการรู้ว่ารอบไหนเป็นรอบไหนจะช่วยให้คุณรู้ว่าโค้ดของคุณควรอยู่ตรงไหน
- คอมไพล์ ทำงานเพียงครั้งเดียวกับองค์ประกอบเทมเพลต ก่อนที่จะมีขอบเขตใดๆ เหมาะสำหรับการแก้ไขโครงสร้างที่ใช้ร่วมกันโดยทุกสำเนา
- ลิงค์ ฟังก์ชันนี้จะทำงานหนึ่งครั้งต่ออินสแตนซ์ หลังจากที่โคลนเทมเพลตและแนบขอบเขตแล้ว ส่วนของ DOM listener และการอ่านขอบเขตจะอยู่ในส่วนนี้
เนื่องจากเมธอด compile ไม่มีขอบเขตการใช้งาน ดังนั้นสิ่งใดก็ตามที่ต้องการข้อมูลจะต้องอยู่ในเมธอด link ซึ่งเป็นเหตุผลว่าทำไมตัวอย่างด้านล่างจึงกำหนดไว้เช่นนั้น link คนเดียว
เรามาดูตัวอย่างวิธีการที่จะบรรลุเป้าหมายนี้กัน ภาพหน้าจอแสดงหมายเลขสี่จุดด้านล่าง
<!DOCTYPE html> <html> <head> <meta chrset="UTF 8"> <title>Event Registration</title> </head> <body> <script src="https://code.angularjs.org/1.6.9/angular-route.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.js"></script> <script src="https://code.angularjs.org/1.6.9/angular.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <h1> Guru99 Global Event</h1> <div ng-app="DemoApp"> <div ng-guru="">Click Me</div> </div> <script type="text/javascript"> var app = angular.module('DemoApp',[]); app.directive('ngGuru',function(){ return { link:function($scope,element,attrs) { element.bind('click',function () { element.html('You clicked me'); });} }}); </script> </body> </html>
Code คำอธิบาย
- เราใช้ฟังก์ชัน `link` ตามที่กำหนดไว้ใน AngularJS เพื่อให้ไดเร็กทีฟสามารถเข้าถึงเหตุการณ์ใน HTML DOM ได้
- เราใช้คีย์เวิร์ด 'element' เพราะเราต้องการตอบสนองต่อเหตุการณ์สำหรับองค์ประกอบ HTML DOM ซึ่งในกรณีของเราคือองค์ประกอบ "div" จากนั้นเราใช้ฟังก์ชัน "bind" เพื่อเพิ่มฟังก์ชันการทำงานที่กำหนดเองให้กับเหตุการณ์คลิกขององค์ประกอบ คำว่า 'click' เป็นคีย์เวิร์ดที่ใช้เพื่อระบุเหตุการณ์คลิกของตัวควบคุม HTML ใดๆ
- ในที่นี้ เรากำลังบอกว่าเราต้องการแทนที่โค้ด HTML ภายในขององค์ประกอบ ซึ่งในกรณีของเราคือองค์ประกอบ div ด้วยข้อความว่า 'คุณคลิกฉัน'
- ที่นี่เรากำลังกำหนดแท็ก div ของเราเพื่อใช้คำสั่งที่กำหนดเองของ ng-guru
💡 เคล็ดลับ: คำสั่งควรจัดการล้างข้อมูลหลังจากเสร็จสิ้นการทำงาน ลบตัวรับฟังใดๆ ที่มีอายุการใช้งานนานกว่าองค์ประกอบภายใน element.on('$destroy', ...)หรือหน้าเว็บนั้นเกิดการรั่วไหลของหน่วยความจำ
หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้
Output:
- ในตอนแรก ผู้ใช้จะเห็นข้อความ 'Click Me' เนื่องจากนั่นคือสิ่งที่กำหนดไว้ภายในแท็ก div เมื่อคุณคลิกที่แท็ก div นั้น ผลลัพธ์ด้านล่างจะปรากฏขึ้นแทน
ตัวอย่างทั้งหมดข้างต้นใช้คุณสมบัติหนึ่งหรือสองข้อจากวัตถุนิยามเดียวกัน ชุดตัวอย่างทั้งหมดนั้นคุ้มค่าที่จะเก็บรักษาไว้ping พร้อมใช้งาน
การอ้างอิงคุณสมบัติของวัตถุคำจำกัดความคำสั่ง
วัตถุที่ส่งคืนโดยโรงงานสร้างคำสั่งคือ วัตถุคำจำกัดความคำสั่งคุณสมบัติแต่ละอย่างจะตอบคำถามหนึ่งข้อเกี่ยวกับวิธีการทำงานของคำสั่งนั้น
| อสังหาริมทรัพย์ | ค่าเริ่มต้น | สิ่งที่มันไม่ |
|---|---|---|
| จำกัด | 'อีเอ' | รูปแบบใดที่ตรงกัน: E องค์ประกอบ, A คุณลักษณะ, C คลาส, M ความคิดเห็น |
| เทมเพลต | ไม่มี | มาร์กอัปแบบอินไลน์ที่แทรกเข้าไปในองค์ประกอบที่ตรงกัน |
| เทมเพลต URL | ไม่มี | เส้นทางไฟล์เทมเพลต ดึงข้อมูลเพียงครั้งเดียวแล้วจัดเก็บไว้ในแคช |
| ขอบเขต | เท็จ | ค่า false จะใช้ขอบเขตของวัตถุหลักร่วมกัน ค่า true จะสร้างวัตถุลูก และวัตถุจะแยกตัวออกจากกัน |
| ตัวควบคุม | ไม่มี | ตัวสร้างที่เปิดเผย API ที่คำสั่งอื่นๆ อาจต้องการ |
| ตัวควบคุมเป็น | ไม่มี | ชื่อเรียกแทนสำหรับตัวควบคุมภายในเทมเพลต |
| ต้องการ | ไม่มี | ตัวควบคุมของคำสั่งอื่นที่จะแทรก; ^ ค้นหาผู้ปกครอง, ^^ ข้ามตัวเอง, ? เป็นตัวเลือกเสริม |
| ลิงค์ | ไม่มี | ฟังก์ชันต่ออินสแตนซ์: ขอบเขต, องค์ประกอบ, คุณสมบัติ, ตัวควบคุม, transcludeFn |
| รวบรวม | ไม่มี | ทำงานเพียงครั้งเดียวกับเทมเพลตก่อนที่จะมีขอบเขตใดๆ เกิดขึ้น |
| ทรานสคลีด | เท็จ | รักษาโครงสร้างมาร์กอัปภายในดั้งเดิมสำหรับ ng-transclude |
| ลำดับความสำคัญ | 0 | เรียงลำดับการคอมไพล์บนองค์ประกอบที่ใช้ร่วมกัน โดยค่าที่สูงกว่าจะถูกเรียกใช้ก่อน |
| สถานีปลายทาง | เท็จ | หยุดคำสั่งที่มีลำดับความสำคัญต่ำกว่าบนองค์ประกอบนั้น |
| แทนที่ | เท็จ | สลับองค์ประกอบกับเทมเพลต ฟังก์ชันนี้เลิกใช้แล้วตั้งแต่เวอร์ชัน 1.3 |
💡 เคล็ดลับ: เริ่มต้นด้วย restrict, template และ link, เพิ่ม scope เพื่อนำกลับมาใช้ใหม่ controller และ require สำหรับการโทรแบบสั่งการต่อสั่งการ และการออกจากระบบ priority และ terminal คนเดียว
















