ลิงค์เทียบกับคอมไพล์ vs คอนโทรลเลอร์


529

เมื่อคุณสร้างคำสั่งคุณสามารถใส่รหัสลงในคอมไพเลอร์ฟังก์ชั่นลิงค์หรือตัวควบคุม

ในเอกสารพวกเขาอธิบายว่า:

  • รวบรวมและฟังก์ชั่นการเชื่อมโยงจะใช้ในขั้นตอนต่าง ๆ ของวงจรเชิงมุม
  • ตัวควบคุมจะใช้ร่วมกันระหว่างคำสั่ง

อย่างไรก็ตามสำหรับฉันมันยังไม่ชัดเจนรหัสประเภทไหนควรไปที่ไหน

เช่น: ฉันสามารถสร้างฟังก์ชั่นในการคอมไพล์และแนบกับขอบเขตในลิงค์หรือแนบฟังก์ชั่นเข้ากับขอบเขตในคอนโทรลเลอร์ได้หรือไม่?

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



บางทีอาจจะเป็นภาพรวมที่ครอบคลุมมากขึ้นของฟังก์ชั่นสั่ง: สั่งเชิงมุม - เมื่อจะใช้รวบรวมควบคุมก่อนการเชื่อมโยงและการโพสต์ลิงค์
Izhaki

1
ฉันเขียนโพสต์ที่มีไดอะแกรมของวงจรชีวิตของ directive (ระยะการสร้าง) อาจช่วยคน: filimanjaro.com/2014/…
เฉลี่ย Joe

คำตอบ:


470

รวบรวม:

นี่คือขั้นตอนที่ Angular รวบรวมคำสั่งของคุณ ฟังก์ชันการคอมไพล์นี้ถูกเรียกเพียงครั้งเดียวสำหรับแต่ละการอ้างอิงถึงคำสั่งที่กำหนด ตัวอย่างเช่นสมมติว่าคุณกำลังใช้คำสั่ง ng-repeat ng-repeat จะต้องค้นหาองค์ประกอบที่แนบมาแยกส่วน html ที่แนบและสร้างฟังก์ชันเทมเพลต

หากคุณใช้ HandleBars, ขีดเส้นใต้เทมเพลตหรือเทียบเท่ามันก็เหมือนกับการรวบรวมแม่แบบของพวกเขาเพื่อแยกฟังก์ชั่นแม่แบบ ในฟังก์ชันเทมเพลตนี้คุณจะส่งผ่านข้อมูลและค่าส่งคืนของฟังก์ชันนั้นคือ html ที่มีข้อมูลในตำแหน่งที่ถูกต้อง

ขั้นตอนการรวบรวมเป็นขั้นตอนนั้นใน Angular ซึ่งส่งคืนฟังก์ชันเทมเพลต ฟังก์ชันเทมเพลตนี้เป็นมุมเรียกว่าฟังก์ชันการเชื่อมโยง

เฟสเชื่อมโยง:

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

ผู้ควบคุม:

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


67
ในการชี้แจง: รวบรวมคอมไพล์เทมเพลตที่จะใช้ทั่วทั้งหน้า ตัวเชื่อมโยงเชื่อมโยงกับแต่ละอินสแตนซ์ ขวา? ตัวควบคุมจะทำงานระหว่างอินสแตนซ์
Zlatko

4
@CMCDragonkai สำหรับแต่ละcontrollerฟังก์ชั่นคำสั่งจะถูกดำเนินการหลังจากการรวบรวม แต่ก่อน pre-linkในสาขาต้นไม้ DOM ท้องถิ่น นอกจากนี้controllerและpre-linkฟังก์ชั่นที่จะดำเนินการภายในสาขา DOM ท้องถิ่นในบนลงล่างลักษณะ หลังจากนั้นpost-linkจะดำเนินการในลักษณะจากล่างขึ้นบน
Artem Platonov

9
มันเป็นระเบียบถ้าคุณไม่เข้าใจ มีเหตุผลในการทำสิ่งที่มันเป็น
demisx

3
นี่คือคำตอบทางเทคนิคที่ถูกต้อง แต่ฉันยังคงมีคำถามเมื่อฉันควรใช้ฟังก์ชั่นการเชื่อมโยง
นิโคลัสมาร์แชลล์

2
เราควรจะใช้controllerแทนlinkทุกที่หรือไม่? เพื่อที่ฉันไม่จำเป็นต้องเปลี่ยนรหัสในอนาคตหากวิธีการที่จะต้องมีการแบ่งปันหรือตรรกะบางอย่างที่จะนำมาใช้? มีข้อผิดพลาดในการใช้งานcontrollerตลอดเวลาแทนที่จะเชื่อมโยงหรือไม่
JPS

99

ฉันต้องการเพิ่มสิ่งที่หนังสือ O'Reily AngularJS โดยทีมงาน Google กล่าวด้วย:

ตัวควบคุม - สร้างตัวควบคุมที่เผยแพร่ API สำหรับการสื่อสารข้ามคำสั่ง ตัวอย่างที่ดีคือDirective to Directive Communication

ลิงก์ - แก้ไขอินสแตนซ์องค์ประกอบ DOM ผลลัพธ์โดยทางโปรแกรมเพิ่มตัวรับฟังเหตุการณ์และตั้งค่าการโยงข้อมูล

คอมไพล์ - ปรับเปลี่ยนเท็มเพลต DOM สำหรับคุณลักษณะข้ามสำเนาของคำสั่งโดยทางโปรแกรมเช่นเดียวกับเมื่อใช้ใน ng-repeat ฟังก์ชันการคอมไพล์ของคุณยังสามารถส่งคืนฟังก์ชันลิงก์เพื่อแก้ไขอินสแตนซ์องค์ประกอบผลลัพธ์


ลิงก์ thinkster.io ของคุณไม่สามารถดูได้โดยไม่ต้องจ่ายเงิน ไม่ใช่ลิงก์ของฉัน แต่นี่อาจจะเหมาะสมกว่า: toddmotto.com/directive-to-directive-communication-with-require
R. van Twisk

51

A directiveช่วยให้คุณสามารถขยายคำศัพท์ HTML ในแบบที่ประกาศสำหรับการสร้างส่วนประกอบของเว็บ ng-appแอตทริบิวต์เป็นคำสั่งเพื่อให้เป็นและทั้งหมดของng-controller ng- prefixed attributesDirectives สามารถattributes, tagsหรือแม้กระทั่ง,class namescomments

คำสั่งเกิดอย่างไร ( compilationและinstantiation)

คอมไพล์:เราจะใช้compileฟังก์ชั่นทั้งmanipulateDOM ก่อนที่มันจะถูกเรนเดอร์และส่งคืนlinkฟังก์ชั่น (ที่จะจัดการการเชื่อมโยงสำหรับเรา) นี่เป็นสถานที่ที่จะวางวิธีการใด ๆ ที่จำเป็นต้องแบ่งปันให้ทั่วกับinstancesคำสั่งทั้งหมดนี้

ลิงก์:เราจะใช้linkฟังก์ชั่นในการลงทะเบียนผู้ฟังทั้งหมดในองค์ประกอบ DOM เฉพาะ (ที่ถูกโคลนจากเทมเพลต) และตั้งค่าการเชื่อมโยงของเราไปยังหน้า

หากตั้งค่าในcompile()ฟังก์ชั่นพวกเขาจะได้รับการตั้งค่าเพียงครั้งเดียว (ซึ่งมักจะเป็นสิ่งที่คุณต้องการ) หากตั้งค่าในlink()ฟังก์ชั่นพวกเขาจะถูกตั้งค่าทุกครั้งที่องค์ประกอบ HTML ถูกผูกไว้กับข้อมูลใน วัตถุ

<div ng-repeat="i in [0,1,2]">
    <simple>
        <div>Inner content</div>
    </simple>
</div>

app.directive("simple", function(){
   return {
     restrict: "EA",
     transclude:true,
     template:"<div>{{label}}<div ng-transclude></div></div>",        
     compile: function(element, attributes){  
     return {
             pre: function(scope, element, attributes, controller, transcludeFn){

             },
             post: function(scope, element, attributes, controller, transcludeFn){

             }
         }
     },
     controller: function($scope){

     }
   };
});

Compileฟังก์ชันส่งคืนฟังก์ชันpreและpostลิงก์ ในฟังก์ชั่นการเชื่อมโยงล่วงหน้าเรามีแม่แบบอินสแตนซ์และขอบเขตจากcontrollerแต่ยังไม่ได้ผูกกับขอบเขตและยังไม่มีเนื้อหาที่แปลง

Postฟังก์ชั่นการเชื่อมโยงเป็นที่โพสต์ลิงค์เป็นฟังก์ชั่นสุดท้ายในการดำเนินการ ตอนนี้transclusionเสร็จสมบูรณ์และthe template is linked to a scope ตัวเลือกที่เป็นเพียงทางลัดในการตั้งค่าเป็นฟังก์ชั่นview will update with data bound values after the next digest cyclelinkpost-link

ตัวควบคุม: ตัวควบคุมคำสั่งสามารถถูกส่งผ่านไปยังขั้นตอนการเชื่อมโยง / การรวบรวมคำสั่งอื่น มันสามารถถูกฉีดเข้าไปในคำสั่งอื่น ๆ เพื่อใช้ในการสื่อสารระหว่างคำสั่ง

คุณต้องระบุชื่อของคำสั่งที่จะต้อง - มันควรจะถูกผูกไว้กับองค์ประกอบเดียวกันหรือผู้ปกครองของมัน ชื่อสามารถนำหน้าด้วย:

?  Will not raise any error if a mentioned directive does not exist.
^  Will look for the directive on parent elements, if not available on the same element.

ใช้วงเล็บเหลี่ยม[‘directive1′, ‘directive2′, ‘directive3′]เพื่อต้องการตัวควบคุมไดเรกทีฟหลายตัว

var app = angular.module('app', []);

app.controller('MainCtrl', function($scope, $element) {
});

app.directive('parentDirective', function() {
  return {
    restrict: 'E',
    template: '<child-directive></child-directive>',
    controller: function($scope, $element){
      this.variable = "Hi Vinothbabu"
    }
  }
});

app.directive('childDirective', function() {
  return {
    restrict:  'E',
    template: '<h1>I am child</h1>',
    replace: true,
    require: '^parentDirective',
    link: function($scope, $element, attr, parentDirectCtrl){
      //you now have access to parentDirectCtrl.variable
    }
  }
});

1
คุณพูดถึงว่าคุณแสดงวิธีการ parentDirectiveCtrl ในตัวควบคุมของเด็ก ... ตัวอย่างนี้เด็กไม่ได้มีตัวควบคุม แต่ฟังก์ชั่นการเชื่อมโยง ... ฉันไม่ได้ติดอยู่กับปัญหานี้ในปัจจุบันดังนั้นมันอาจจะไม่ สำคัญมาก แต่เป็นคำถามที่อยากรู้
alockwood05

13

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

controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...

เมื่อเทียบกับ

link: function(scope, element, attrs) {... //no services allowed

2
กรุณาแสดงความคิดเห็นเพื่ออธิบายจุดของคุณเมื่อคุณลงคะแนนคำตอบ ขอบคุณ
svassr

53
ฉันไม่ได้ downvoter แต่นี้ไม่ถูกต้องอย่างเคร่งครัดเพราะคุณยังสามารถฉีดพึ่งพาจำเป็นใด ๆ module.directive('myDirective', function($window) { etc...ที่เป็นคำสั่งของตัวเองเช่น: ซึ่งสามารถเข้าถึงได้จากภายในฟังก์ชั่นลิงค์
Mike Chamberlain

1
ดูเหมือนว่าจะไม่ถูกต้องอย่างตรงไปตรงมาในขณะที่คุณสามารถฉีดบริการลงในฟังก์ชั่นลิงค์
Code Whisperer

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

1
@ cwyatt1 ฉันแก้ไข parlance, plnkr ไม่แสดงการฉีดเข้าไปในฟังก์ชั่น link () เพราะนั่นไม่ใช่ฟีเจอร์ Angular คุณอาจคิดว่าฉันเป็นคนที่หยาบคาย แต่ความคิดเห็น metamatts ได้สรุปความแตกต่างที่สำคัญมากมายระหว่างสิ่งที่ plunkr ทำและสิ่งที่ฉีดเข้าไปในคอนโทรลเลอร์ OP กำลังถามว่าอะไรคือความแตกต่างและมีความแตกต่าง
Josh Ribakoff

10

นี่เป็นตัวอย่างที่ดีสำหรับขั้นตอนการทำความเข้าใจคำสั่ง http://codepen.io/anon/pen/oXMdBQ?editors=101

var app = angular.module('myapp', [])

app.directive('slngStylePrelink', function() {
    return {
        scope: {
            drctvName: '@'
        },
        controller: function($scope) {
            console.log('controller for ', $scope.drctvName);
        },
        compile: function(element, attr) {
            console.log("compile for ", attr.name)
            return {
                post: function($scope, element, attr) {
                    console.log('post link for ', attr.name)
                },
                pre: function($scope, element, attr) {
                    $scope.element = element;
                    console.log('pre link for ', attr.name)
                        // from angular.js 1.4.1
                    function ngStyleWatchAction(newStyles, oldStyles) {
                        if (oldStyles && (newStyles !== oldStyles)) {
                            forEach(oldStyles, function(val, style) {
                                element.css(style, '');
                            });
                        }
                        if (newStyles) element.css(newStyles);
                    }

                    $scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);

                    // Run immediately, because the watcher's first run is async
                    ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
                }
            };
        }
    };
});

HTML

<body ng-app="myapp">
    <div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
        <div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
        </div>
    </div>
</body>

4
คุณสามารถอธิบายรายละเอียดเกี่ยวกับเหตุผลที่โค้ดตัวอย่างนี้จะช่วยให้เข้าใจความแตกต่างระหว่างlink, compileและcontroller?
cel sharp

คุณรู้หรือไม่ว่าrequireคำสั่ง d สามารถถูกฉีดเข้าไปในคอนโทรลเลอร์ของคำสั่งพึ่งพาได้อย่างไร?
alockwood05

คุณเข้ารหัสตัวอย่าง: ข้อผิดพลาดที่ไม่ได้บันทึก: [$ injector: modulerr] ไม่สามารถสร้างอินสแตนซ์โมดูล myapp เนื่องจาก: ข้อผิดพลาด: [$ injector: unpr] ผู้ให้บริการที่ไม่รู้จัก: slngStylePrelinkProvider
rofrol

7
  • คอมไพล์ : ใช้เมื่อเราต้องการแก้ไขเทมเพลตคำสั่งเช่นเพิ่มนิพจน์ใหม่ผนวกคำสั่งอื่นในคำสั่งนี้
  • คอนโทรลเลอร์ : ใช้เมื่อเราต้องการแบ่งปัน / ใช้ข้อมูลขอบเขต $ อีกครั้ง
  • ลิงค์ : มันเป็นฟังก์ชั่นที่ใช้เมื่อเราต้องการแนบตัวจัดการเหตุการณ์หรือเพื่อจัดการ DOM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.