ไดเร็กทีฟแบบกำหนดเองของ AngularJS: เทมเพลต ขอบเขต และการแทรกข้อมูล

⚡ สรุปอย่างชาญฉลาด

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

  • 🏗️ การลงทะเบียน: เมธอด module.directive รับชื่อที่ถูกทำให้เป็นมาตรฐานแล้ว บวกกับฟังก์ชันโรงงานที่ส่งคืนอ็อบเจ็กต์คำจำกัดความของไดเร็กทีฟ
  • 🔤 การตั้งชื่อ: ชื่อภายในใช้รูปแบบ camelcase Javaสคริปต์และเคบับเคสภายใน HTML ดังนั้น ngGuru ในหน้าเว็บเขียนว่า ng-guru
  • 🎯 จับคู่: คุณสมบัติ restrict จะกำหนดว่า AngularJS จะจับคู่กับองค์ประกอบ คุณลักษณะ คลาส หรือความคิดเห็นหรือไม่ โดยค่าเริ่มต้นจะเป็น EA
  • 🧬 ขอบเขตการเข้าถึง: โดยปกติแล้ว directive จะใช้ขอบเขตเดียวกันกับ parent scope ในขณะที่ตัวเลือก scope จะสร้างขอบเขตแบบ isolate โดยใช้การผูก @, =, & หรือ <
  • 🔗 ฟังก์ชันการเชื่อมโยง: ฟังก์ชัน link รับค่า scope, element และ attrs และเป็นตำแหน่งที่เหมาะสมสำหรับการใช้งาน DOM listener และการจัดการ cleanup
  • 🧱 ส่วนประกอบ: คำสั่งต่างๆ สามารถซ้อนกันได้ ห่อหุ้มมาร์กอัปแบบต่างๆ ผ่านการแทรกสอด และเปิดเผย API ให้กับองค์ประกอบระดับเดียวกันผ่านคอนโทรลเลอร์
  • ⚠️ สถานะการสนับสนุน: AngularJS สิ้นสุดการสนับสนุนเมื่อวันที่ 31 ธันวาคม 2021 และจะไม่ได้รับการแก้ไขข้อบกพร่องหรือการแก้ไขด้านความปลอดภัยเพิ่มเติมอีกต่อไป

ไดเร็กทีฟแบบกำหนดเองใน 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 คำอธิบาย

  1. เรากำลังสร้าง a โมดูล สำหรับแอปพลิเคชัน AngularJS ของเรา จำเป็นต้องสร้าง directive แบบกำหนดเอง เนื่องจาก directive นั้นจะถูกสร้างขึ้นโดยใช้โมดูลนี้
  2. ขณะนี้เรากำลังสร้างไดเร็กทีฟแบบกำหนดเองชื่อ “ng”Guru“และกำหนดฟังก์ชันโรงงานที่จะเก็บโค้ดที่กำหนดเองสำหรับไดเร็กทีฟของเรา”
  3. เราใช้พารามิเตอร์เทมเพลต ซึ่งเป็นพารามิเตอร์ที่ AngularJS กำหนดไว้สำหรับไดเร็กทีฟแบบกำหนดเอง ในที่นี้ เรากำหนดว่าเมื่อใดก็ตามที่ใช้ไดเร็กทีฟนี้ ให้ใช้ค่าของเทมเพลตและแทรกเข้าไปในโค้ดที่เรียกใช้
  4. ในขั้นตอนนี้ เราจะใช้คำสั่ง “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: '&nbsp;&nbsp;Name <input type="text"><br><br>&nbsp;&nbsp;&nbsp;Age<input type="text">'
        };
    });

</script>

</body>
</html>

Code คำอธิบาย

  • ในโค้ดตัวอย่างสำหรับ directive แบบกำหนดเอง สิ่งที่เปลี่ยนแปลงคือค่าที่ส่งให้กับพารามิเตอร์ template เท่านั้น แทนที่จะใช้แท็กธรรมดาหรือข้อความบรรทัดเดียว เราป้อนส่วนของช่องป้อนข้อมูลสองช่องสำหรับ "ชื่อ" และ "อายุ" ที่ต้องแสดงบนหน้าเว็บของเรา

💡 เคล็ดลับ: เทมเพลตที่ยาวขนาดนี้ควรอยู่ในไฟล์แยกต่างหาก สลับ template สำหรับ templateUrl: 'name-age.html'.

หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้

Output:

สร้างคำสั่งที่ใช้ซ้ำได้

จากผลลัพธ์ข้างต้น เราจะเห็นว่าโค้ดส่วนหนึ่งจากเทมเพลตของไดเร็กทีฟแบบกำหนดเองถูกเพิ่มเข้าไปในหน้าเว็บแล้ว เทมเพลตยังคงเป็นแบบคงที่ ดังนั้นขั้นตอนต่อไปคือการป้อนข้อมูลเข้าไปในเทมเพลต

ไดเร็กทีฟและสโคปของ AngularJS

การขอ ขอบเขต หมายถึง ตัวเชื่อมที่ยึดตัวควบคุม (Controller) กับมุมมอง (View) โดยทำหน้าที่จัดการข้อมูลระหว่างมุมมองและตัวควบคุม

เมื่อสร้างไดเร็กทีฟ AngularJS แบบกำหนดเอง โดยค่าเริ่มต้น ไดเร็กทีฟเหล่านั้นจะสามารถเข้าถึงอ็อบเจ็กต์ขอบเขต (scope object) ในคอนโทรลเลอร์หลักได้ ซึ่งเป็นอ็อบเจ็กต์ขอบเขตเดียวกัน ไม่ใช่สำเนา

ด้วยวิธีนี้ จึงกลายเป็นเรื่องง่ายสำหรับคำสั่งแบบกำหนดเองในการใช้ประโยชน์จากข้อมูลที่ส่งผ่านไปยังตัวควบคุมหลัก

เรามาดูตัวอย่างวิธีการใช้ขอบเขตของคอนโทรลเลอร์หลักในไดเร็กทีฟที่กำหนดเองของเรากัน ภาพหน้าจอด้านล่างแสดงขั้นตอนทั้งสี่ขั้นตอน

ไดเร็กทีฟและสโคปของ 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-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 คำอธิบาย

  1. ขั้นแรก เราสร้างคอนโทรลเลอร์ชื่อ “DemoController” ในคอนโทรลเลอร์นี้ เรากำหนดตัวแปรชื่อ tutorialName และเชื่อมโยงตัวแปรนี้เข้ากับอ็อบเจ็กต์ขอบเขต (scope object) ด้วยคำสั่งเดียว: $scope.tutorialName = "Angular JS".
  2. ในคำสั่งที่กำหนดเองของเรา เราสามารถเรียกตัวแปร “tutorialName” ได้โดยใช้นิพจน์ ตัวแปรนี้จะสามารถเข้าถึงได้เนื่องจากมีการกำหนดไว้ในคอนโทรลเลอร์ “DemoController” ซึ่งจะกลายเป็นพาเรนต์สำหรับคำสั่งนี้
  3. เราอ้างอิงตัวควบคุมในแท็ก div ซึ่งจะทำหน้าที่เป็นแท็ก div หลักของเรา โปรดทราบว่าจะต้องดำเนินการนี้ก่อนเพื่อให้คำสั่งที่กำหนดเองของเราเข้าถึงตัวแปร TutorialName
  4. ในที่สุดเราก็แนบคำสั่งที่กำหนดเอง “ng-guru” เข้ากับแท็ก div ของเรา

หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้

Output:

ไดเร็กทีฟและสโคปของ AngularJS

ผลลัพธ์ข้างต้นแสดงให้เห็นอย่างชัดเจนว่า 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 คำอธิบาย

  1. ขั้นแรก เราสร้างคอนโทรลเลอร์ชื่อ “DemoController” ในคอนโทรลเลอร์นี้ เราจะกำหนดตัวแปรชื่อ “tutorialName” และคราวนี้ แทนที่จะแนบตัวแปรนี้เข้ากับอ็อบเจ็กต์ขอบเขต เราจะแนบตัวแปรนี้เข้ากับคอนโทรลเลอร์โดยตรง
  2. ในคำสั่งที่กำหนดเองของเรา เราได้กล่าวถึงเป็นพิเศษว่าเราต้องการใช้คอนโทรลเลอร์ “DemoController” โดยใช้คีย์เวิร์ดพารามิเตอร์คอนโทรลเลอร์
  3. เราสร้างการอ้างอิงถึงคอนโทรลเลอร์โดยใช้พารามิเตอร์ “controllerAs” ซึ่งเป็นพารามิเตอร์ที่กำหนดโดย AngularJS และเป็นวิธีในการอ้างอิงคอนโทรลเลอร์โดยใช้ชื่อแทน
  4. สุดท้ายนี้ ในเทมเพลตของเรา เราใช้ข้อมูลอ้างอิงที่สร้างขึ้นในขั้นตอนที่ 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 จับทุกอย่างที่อยู่ภายในแท็กที่กำหนดเองในมาร์กอัป และนำไปใส่ในเทมเพลต ณ จุดนั้น"

เรามาดูตัวอย่างวิธีการบรรลุเป้าหมายนี้กัน ภาพหน้าจอแสดงหมายเลขห้าประเด็นต่อไปนี้

คำสั่งและส่วนประกอบ 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 คำอธิบาย

  1. เราใช้คำสั่งเพื่อกำหนดแท็ก HTML แบบกำหนดเองชื่อ “pane” โดยใช้ฟังก์ชันโรงงาน (factory function) ในการป้อนโค้ด ในผลลัพธ์ แท็ก pane จะแสดงข้อความ “Angular JS” ในกรอบสี่เหลี่ยมที่มีเส้นขอบสีดำทึบ
  2. คุณสมบัติ “transclude” ต้องตั้งค่าเป็น true ซึ่งเป็นการบอกให้ AngularJS เก็บมาร์กอัปภายในเดิมไว้และทำให้สามารถใช้งานได้ ng-transclude.
  3. ในออบเจ็กต์ขอบเขต เรากำลังประกาศการผูกชื่อเรื่อง โดยปกติแล้วแอตทริบิวต์จะเขียนในรูปแบบคู่ชื่อ/ค่า เช่น name="value"ในกรณีของเรา แอตทริบิวต์บนแท็ก pane คือ "title" และสัญลักษณ์ "@" สั่งให้ AngularJS คัดลอกแอตทริบิวต์นั้นมาเป็นสตริงที่แทรกเข้าไป
  4. เทมเพลตจะวาดเส้นขอบสีดำทึบให้กับส่วนควบคุมของเรา และทำเครื่องหมายตำแหน่งที่จะแทรกมาร์กอัปที่แทรกเข้ามา
  5. สุดท้ายนี้ เราจะเรียกใช้แท็ก HTML ที่กำหนดเอง พร้อมกับแอตทริบิวต์ title ที่ได้กำหนดไว้

⚠️ การแก้ไข: ข้อความต้นฉบับระบุว่า “@” คือ “ข้อกำหนดจาก Angular” ไม่ใช่เพียงแค่พิธีการ: @ เป็นหนึ่งในสี่สัญลักษณ์การผูกขอบเขตแบบแยกส่วนในตารางด้านบน และจะส่งคืนค่าเป็นสตริงเสมอ เทมเพลตนี้จะไม่แสดงผลเช่นกัน {{title}}และไม่มีตัวควบคุมใดกำหนด titleดังนั้นจึงมองเห็นเฉพาะข้อความที่ถูกแทรกเข้าไปเท่านั้น

หากโค้ดทำงานสำเร็จ ผลลัพธ์จะปรากฏดังนี้

Output:

คำสั่งและส่วนประกอบ AngularJS

  • ผลลัพธ์แสดงให้เห็นองค์ประกอบ 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 คำอธิบาย

  1. เรากำลังสร้างคำสั่งที่เรียกว่า "outer" ซึ่งจะทำหน้าที่เป็นคำสั่งหลักของเรา คำสั่งนี้จะทำการเรียกคำสั่ง "ภายใน"
  2. การขอ restrict:'E' ตัวเลือกนี้บอกให้ AngularJS จับคู่แต่ละไดเร็กทีฟโดย ธาตุ ชื่อ ซึ่งเป็นเหตุผลที่สามารถเขียนเป็นแท็กได้ <outer> และ <inner>ตัวอักษร 'E' เป็นตัวย่อของคำว่า 'Element'
  3. ที่นี่เรากำลังสร้างคำสั่งภายในซึ่งแสดงข้อความ “ภายใน” ในแท็ก div
  4. ในเทมเพลตสำหรับไดเร็กทีฟภายนอก เรากำลังเรียกใช้ไดเร็กทีฟภายใน ดังนั้นตรงนี้เราจึงกำลังแทรกเทมเพลตจากไดเร็กทีฟภายในเข้าไปในไดเร็กทีฟภายนอก
  5. สุดท้ายนี้ เรากำลังเรียกคำสั่งภายนอกโดยตรง

⚠️ การแก้ไข: ข้อความต้นฉบับอ้างว่า 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 คำอธิบาย

  1. เราใช้ฟังก์ชัน `link` ตามที่กำหนดไว้ใน AngularJS เพื่อให้ไดเร็กทีฟสามารถเข้าถึงเหตุการณ์ใน HTML DOM ได้
  2. เราใช้คีย์เวิร์ด 'element' เพราะเราต้องการตอบสนองต่อเหตุการณ์สำหรับองค์ประกอบ HTML DOM ซึ่งในกรณีของเราคือองค์ประกอบ "div" จากนั้นเราใช้ฟังก์ชัน "bind" เพื่อเพิ่มฟังก์ชันการทำงานที่กำหนดเองให้กับเหตุการณ์คลิกขององค์ประกอบ คำว่า 'click' เป็นคีย์เวิร์ดที่ใช้เพื่อระบุเหตุการณ์คลิกของตัวควบคุม HTML ใดๆ
  3. ในที่นี้ เรากำลังบอกว่าเราต้องการแทนที่โค้ด HTML ภายในขององค์ประกอบ ซึ่งในกรณีของเราคือองค์ประกอบ div ด้วยข้อความว่า 'คุณคลิกฉัน'
  4. ที่นี่เรากำลังกำหนดแท็ก 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 คนเดียว

คำถามที่พบบ่อย

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

โดยปกติชื่อจะไม่ได้ถูกทำให้เป็นมาตรฐานอย่างถูกต้อง หรือข้อจำกัดจะยกเว้นรูปแบบที่คุณใช้ คำสั่งที่ลงทะเบียนเป็นของฉันBox ตรงกับ my-box ไม่ใช่ mybox และแท็กองค์ประกอบจำเป็นต้องจำกัดให้รวม E ด้วย

ใช่. AI ตัวช่วยจะแมปขอบเขตแยก (isolate scope) เข้ากับตัวตกแต่งอินพุตและเอาต์พุต (Input and Output decorator) และแมปเทมเพลตเข้ากับเทมเพลตส่วนประกอบ (component template) Revตรวจสอบโค้ด DOM สำหรับการแทรกและการเชื่อมโยงด้วยตนเอง เนื่องจากโค้ดเหล่านั้นมักไม่สามารถแปลงได้อย่างราบรื่น

ผู้ช่วย AI สร้าง การทดสอบหน่วย โครงร่างเหล่านี้จะรวบรวมคำสั่ง กำหนดขอบเขต และตรวจสอบผลลัพธ์ที่แสดงผล โครงร่างเหล่านี้มีประสิทธิภาพในการระบุกรณีพิเศษ ดังนั้นให้ถือว่าการตรวจสอบที่สร้างขึ้นเป็นเพียงร่างเริ่มต้น

ใช้ templateUrl เมื่อโค้ด HTML ยาวเกินหนึ่งหรือสองบรรทัด ไฟล์นี้สามารถแก้ไขได้ด้วยเครื่องมือ HTML ทั่วไป และจะถูกดึงมาเพียงครั้งเดียว จากนั้นจะดึงมาจากแคชเทมเพลตสำหรับการใช้งานในครั้งต่อๆ ไป

สรุปโพสต์นี้ด้วย: