การส่งผ่านคำสั่งเชิงมุมเชิงมุมกับฟังก์ชันในนิพจน์ที่ระบุในแอตทริบิวต์ของคำสั่งสามารถทำได้หรือไม่?


160

ฉันมีคำสั่งแบบฟอร์มที่ใช้callbackคุณลักษณะที่ระบุกับขอบเขต isolate:

scope: { callback: '&' }

มันอยู่ภายในng-repeatดังนั้นการแสดงออกที่ฉันผ่านรวมถึงidวัตถุที่เป็นข้อโต้แย้งกับฟังก์ชั่นการโทรกลับ:

<directive ng-repeat = "item in stuff" callback = "callback(item.id)"/>

เมื่อฉันทำตามคำสั่งเสร็จแล้วจะเรียกใช้$scope.callback()จากฟังก์ชั่นควบคุม สำหรับกรณีส่วนใหญ่สิ่งนี้เป็นเรื่องปกติและทุกอย่างที่ฉันต้องการจะทำ แต่บางครั้งฉันก็ต้องการที่จะเพิ่มการโต้แย้งจากภายในdirectiveตัวเอง

มีการแสดงออกเชิงมุมที่จะช่วยให้นี้: $scope.callback(arg2)ส่งผลให้callbackถูกเรียกด้วยarguments = [item.id, arg2]?

ถ้าไม่เป็นเช่นนั้นวิธีที่ประณีตที่สุดในการทำเช่นนี้คืออะไร?

ฉันพบว่าใช้งานได้:

<directive 
  ng-repeat = "item in stuff" 
  callback = "callback" 
  callback-arg="item.id"/>

กับ

scope { callback: '=', callbackArg: '=' }

และคำสั่งโทร

$scope.callback.apply(null, [$scope.callbackArg].concat([arg2, arg3]) );

แต่ฉันไม่คิดว่ามันประณีตโดยเฉพาะและเกี่ยวข้องกับการวางสิ่งพิเศษไว้ในขอบเขตไอโซเลท

มีวิธีที่ดีกว่า?

Plunker playground ที่นี่ (เปิดคอนโซล)


แอตทริบิวต์การตั้งชื่อ "callback =" misleads มันเป็นการประเมินผลการโทรกลับไม่ใช่การติดต่อกลับ
Dmitri Zaitsev

@DmitriZaitsev เป็นนิพจน์เชิงมุมการติดต่อกลับที่จะประเมินเป็นฟังก์ชัน JavaScript ฉันคิดว่ามันค่อนข้างชัดเจนว่าไม่ใช่ฟังก์ชัน JavaScript ในตัวเอง เป็นเพียงการตั้งค่า แต่ฉันไม่ต้องการต่อท้ายคุณลักษณะทั้งหมดของฉันด้วย "-expression" สิ่งนี้สอดคล้องกับngAPI ตัวอย่างเช่นng-click="someFunction()"นิพจน์ที่ประเมินว่าจะเรียกใช้ฟังก์ชัน
Ed Hinchliffe

ฉันไม่เคยเห็นนิพจน์เชิงมุมเรียกว่า "โทรกลับ" มันมักจะเป็นฟังก์ชั่นที่คุณส่งผ่านเพื่อถูกเรียกมาจากชื่อ คุณยังใช้ฟังก์ชันที่เรียกว่า "callback" ในตัวอย่างของคุณเพื่อทำให้สิ่งต่าง ๆ สับสนยิ่งขึ้น
Dmitri Zaitsev

ฉันไม่แน่ใจว่าคุณสับสนหรือไม่ ในตัวอย่างของฉัน$scope.callbackถูกกำหนดโดยcallback="someFunction"คุณลักษณะและscope: { callback: '=' }คุณสมบัติของวัตถุคำจำกัดความคำสั่ง $scope.callback เป็นฟังก์ชันที่จะเรียกใช้ในภายหลัง ค่าแอททริบิวที่แท้จริงคือสตริง - ซึ่งเป็นกรณีที่มี HTML เสมอ
Ed Hinchliffe

คุณตั้งชื่อทั้งคุณสมบัติและฟังก์ชั่นเดียวกัน - "โทรกลับ" นั่นเป็นสูตรสำหรับความสับสน ง่ายต่อการหลีกเลี่ยงจริงๆ
Dmitri Zaitsev

คำตอบ:


215

หากคุณประกาศโทรกลับตามที่กล่าวไว้โดย @ lex82 เช่น

callback = "callback(item.id, arg2)"

คุณสามารถเรียกใช้เมธอด callback ในขอบเขต directive ด้วยการแมปวัตถุและมันจะทำการผูกอย่างถูกต้อง ชอบ

scope.callback({arg2:"some value"});

โดยไม่ต้องใช้ $ parse ดูซอของฉัน (บันทึกคอนโซล) http://jsfiddle.net/k7czc/2/

อัปเดต : มีตัวอย่างเล็ก ๆ ในเอกสารประกอบ :

& หรือ & attr - จัดให้มีวิธีการดำเนินการแสดงออกในบริบทของขอบเขตผู้ปกครอง หากไม่ได้ระบุชื่อ attr ชื่อของแอตทริบิวต์นั้นจะถือว่าเป็นชื่อท้องถิ่น กำหนดและนิยามวิดเจ็ตของขอบเขต: {localFn: '& myAttr'} จากนั้นแยกคุณสมบัติขอบเขต localFn จะชี้ไปที่ wrapper ฟังก์ชันสำหรับนิพจน์ count = count + value บ่อยครั้งที่ต้องการส่งข้อมูลจากขอบเขตแยกผ่านนิพจน์และไปยังขอบเขตพาเรนต์สิ่งนี้สามารถทำได้โดยส่งแผนที่ของชื่อตัวแปรและค่าท้องถิ่นไปยังนิพจน์ wrapper fn ตัวอย่างเช่นถ้าการแสดงออกคือการเพิ่มขึ้น (จำนวน) จากนั้นเราสามารถระบุมูลค่าจำนวนโดยการเรียก localFn เป็น localFn ({จำนวน: 22})


4
ดีมาก! เอกสารนี้มีอยู่ทุกที่หรือไม่?
ACH

12
ฉันไม่คิดว่ามันเป็นทางออกที่ดีเพราะในคำจำกัดความของคำสั่งบางครั้งคุณอาจไม่รู้ว่าพารามิเตอร์ส่งผ่านอะไร
OMGPOP

นี่เป็นวิธีแก้ปัญหาที่ดีและขอบคุณสำหรับสิ่งนั้น แต่ฉันเชื่อว่าคำตอบนั้นต้องการการเติมเต็ม ใครคือ lex82 และเขาพูดถึงอะไร?
Wtower

แนวทางที่น่าสนใจ แม้ว่าจะเกิดอะไรขึ้นเมื่อคุณต้องการอนุญาตให้ฟังก์ชันใด ๆ ที่มีพารามิเตอร์ ANY (หรือหลายรายการ) ถูกส่งผ่าน? คุณไม่รู้อะไรเลยเกี่ยวกับฟังก์ชั่นหรือพารามิเตอร์และจำเป็นต้องรันมันในบางเหตุการณ์ภายในคำสั่ง จะไปเกี่ยวกับมันได้อย่างไร ตัวอย่างเช่นในคำสั่งคุณสามารถมี onchangefunc = 'myCtrlFunc (dynamicVariableHere)'
trainoasis

58

ไม่มีอะไรผิดปกติกับคำตอบอื่น ๆ แต่ฉันใช้เทคนิคต่อไปนี้เมื่อผ่านฟังก์ชั่นในแอตทริบิวต์คำสั่ง

ออกจากวงเล็บเมื่อรวมถึงคำสั่งใน html ของคุณ:

<my-directive callback="someFunction" />

จากนั้น "แกะ" ฟังก์ชันในลิงก์หรือตัวควบคุมของคำสั่งของคุณ นี่คือตัวอย่าง:

app.directive("myDirective", function() {

    return {
        restrict: "E",
        scope: {
            callback: "&"                              
        },
        template: "<div ng-click='callback(data)'></div>", // call function this way...
        link: function(scope, element, attrs) {
            // unwrap the function
            scope.callback = scope.callback(); 

            scope.data = "data from somewhere";

            element.bind("click",function() {
                scope.$apply(function() {
                    callback(data);                        // ...or this way
                });
            });
        }
    }
}]);    

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

<outer-directive callback="someFunction" >
    <middle-directive callback="callback" >
        <inner-directive callback="callback" />
    </middle-directive>
</outer-directive>

จากนั้นคุณจะพบสิ่งเช่นนี้ในคำสั่งภายในของคุณ:

callback()()()(data); 

ซึ่งจะล้มเหลวในสถานการณ์การซ้อนอื่น ๆ

ฉันดัดแปลงเทคนิคนี้จากบทความที่ยอดเยี่ยมโดย Dan Wahlin ที่http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters

ฉันได้เพิ่มขั้นตอนการแกะเพื่อทำให้การเรียกฟังก์ชั่นเป็นธรรมชาติมากขึ้นและเพื่อแก้ปัญหาการซ้อนที่ฉันพบในโครงการ


2
วิธีการที่ดี แต่ฉันไม่สามารถใช้thisตัวชี้ภายในวิธีการโทรกลับเพราะใช้ขอบเขตของคำสั่ง ฉันใช้ typescript และการโทรกลับของฉันมีลักษณะเช่นนี้:public validateFirstName(firstName: string, fieldName: string): ng.IPromise<boolean> { var deferred = this.mQService.defer<boolean>(); ... .then(() => deferred.resolve(true)) .catch((msg) => { deferred.reject(false); }); return deferred.promise; }
ndee

1
หมายเหตุ: หากคุณมีคำสั่งซ้อนกันและต้องการกระจายการติดต่อกลับขึ้นไปคุณต้องแกะในแต่ละคำสั่งไม่ใช่เพียงคำสั่งเดียวที่กระตุ้นการโทรกลับ
Episodex

43

ในคำสั่ง ( myDirective):

...
directive.scope = {  
    boundFunction: '&',
    model: '=',
};
...
return directive;

ในเทมเพลตคำสั่ง:

<div 
data-ng-repeat="item in model"  
data-ng-click='boundFunction({param: item})'>
{{item.myValue}}
</div>

ในแหล่งที่มา:

<my-directive 
model='myData' 
bound-function='myFunction(param)'>
</my-directive>

... โดยที่myFunctionกำหนดไว้ในคอนโทรลเลอร์

โปรดทราบว่าparamในผูกแม่แบบสั่งเรียบร้อยไปในแหล่งที่มาและมีการตั้งค่าparamitem


หากต้องการโทรจากภายในlinkคุณสมบัติของคำสั่ง ("ข้างใน") ให้ใช้วิธีการที่คล้ายกันมาก:

...
directive.link = function(isolatedScope) {
    isolatedScope.boundFunction({param: "foo"});
};
...
return directive;

ขณะที่มีแหล่งที่มา: bound-function = 'myFunction (obj1.param, obj2.param)'> แล้วจะดำเนินการอย่างไร?
Ankit Pandey

15

ใช่มีวิธีที่ดีกว่า: คุณสามารถใช้บริการ $ parseในคำสั่งของคุณเพื่อประเมินการแสดงออกในบริบทของขอบเขตหลักในขณะที่ผูกตัวระบุบางอย่างในการแสดงออกกับค่าที่มองเห็นได้เฉพาะในคำสั่งของคุณ:

$parse(attributes.callback)(scope.$parent, { arg2: yourSecondArgument });

เพิ่มบรรทัดนี้ไปยังฟังก์ชันลิงก์ของคำสั่งที่คุณสามารถเข้าถึงแอตทริบิวต์ของคำสั่ง

แอ็ตทริบิวต์การเรียกกลับของคุณอาจถูกตั้งค่าเช่นcallback = "callback(item.id, arg2)"เนื่องจาก arg2 ถูกผูกไว้กับ yourSecondArgument โดยบริการ $ parse ภายในคำสั่ง คำสั่งเช่นng-clickให้คุณเข้าถึงเหตุการณ์คลิกผ่าน$eventตัวระบุภายในนิพจน์ที่ส่งไปยังคำสั่งโดยใช้กลไกนี้

โปรดทราบว่าคุณไม่จำเป็นต้องcallbackเป็นสมาชิกของขอบเขตแยกของคุณด้วยโซลูชันนี้


3
การใช้scope.$parentทำให้คำสั่ง "รั่วไหล" - "รู้" มากเกินไปของโลกภายนอกซึ่งองค์ประกอบที่ห่อหุ้มที่ออกแบบมาอย่างดีไม่ควร
Dmitri Zaitsev

3
มันรู้ว่ามันมีขอบเขตพาเรนต์ แต่มันไม่สามารถเข้าถึงฟิลด์ใดฟิลด์หนึ่งในขอบเขตได้ฉันจึงคิดว่ามันทนได้
lex82

0

สำหรับฉันทำงานต่อไปนี้:

ในคำสั่งให้ประกาศอย่างนี้:

.directive('myDirective', function() {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            myFunction: '=',
        },
        templateUrl: 'myDirective.html'
    };
})  

ในเทมเพลตคำสั่งใช้ด้วยวิธีดังต่อไปนี้:

<select ng-change="myFunction(selectedAmount)">

จากนั้นเมื่อคุณใช้คำสั่งส่งผ่านฟังก์ชันดังนี้:

<data-my-directive
    data-my-function="setSelectedAmount">
</data-my-directive>

คุณผ่านฟังก์ชั่นโดยการประกาศและมันถูกเรียกจากคำสั่งและพารามิเตอร์ที่มีประชากร

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