Một chỉ thị góc có thể truyền đối số cho các hàm trong các biểu thức được chỉ định trong các thuộc tính của lệnh không?


160

Tôi có một chỉ thị biểu mẫu sử dụng một callbackthuộc tính được chỉ định với phạm vi cô lập:

scope: { callback: '&' }

Nó nằm bên trong một ng-repeatbiểu thức mà tôi truyền vào bao gồm idđối tượng làm đối số cho hàm gọi lại:

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

Khi tôi hoàn thành lệnh này, nó gọi $scope.callback()từ chức năng điều khiển của nó. Đối với hầu hết các trường hợp, điều này là tốt, và đó là tất cả những gì tôi muốn làm, nhưng đôi khi tôi muốn thêm một đối số khác từ bên trong directivechính nó.

Có một biểu thức góc nào cho phép điều này : $scope.callback(arg2), dẫn đến callbackđược gọi bằng arguments = [item.id, arg2]?

Nếu không, cách gọn gàng nhất để làm điều này là gì?

Tôi đã thấy rằng điều này hoạt động:

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

Với

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

và lệnh gọi

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

Nhưng tôi không nghĩ nó đặc biệt gọn gàng và nó liên quan đến việc đưa thêm những thứ khác vào phạm vi cô lập.

Có cách nào tốt hơn?

Sân chơi Plunker ở đây (mở giao diện điều khiển).


Thuộc tính đặt tên "callback =" đánh lừa. Nó thực sự là một đánh giá gọi lại, không phải là một cuộc gọi lại chính nó.
Dmitri Zaitsev

@DmitriZaitsev đó là biểu thức góc gọi lại sẽ đánh giá hàm JavaScript. Tôi nghĩ khá rõ ràng rằng bản thân nó không phải là một hàm JavaScript. Đó chỉ là sở thích nhưng tôi không muốn phải có tất cả các thuộc tính của mình với "-expression". Điều này phù hợp với ngAPI, ví dụ, ng-click="someFunction()"một biểu thức đánh giá việc thực thi một chức năng.
Ed Hinchliffe

Tôi chưa bao giờ thấy biểu hiện Angular gọi là "gọi lại". Nó luôn luôn là một chức năng mà bạn vượt qua để được gọi, từ đó tên. Bạn thậm chí sử dụng một hàm gọi là "gọi lại" trong ví dụ của mình, để khiến mọi thứ trở nên khó hiểu hơn.
Dmitri Zaitsev

Tôi không chắc bạn có nhầm lẫn hay không. Trong ví dụ của tôi $scope.callbackđược đặt bởi callback="someFunction"thuộc tính và thuộc tính scope: { callback: '=' }của đối tượng định nghĩa chỉ thị. $scope.callback một chức năng được gọi vào một ngày sau đó. Giá trị thuộc tính thực tế rõ ràng là một chuỗi - luôn luôn như vậy với HTML.
Ed Hinchliffe

Bạn đặt tên cho cả hai thuộc tính và chức năng giống nhau - "gọi lại". Đó là công thức cho sự nhầm lẫn. Dễ tránh thực sự.
Dmitri Zaitsev

Câu trả lời:


215

Nếu bạn khai báo cuộc gọi lại như được đề cập bởi @ lex82 như

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

Bạn có thể gọi phương thức gọi lại trong phạm vi chỉ thị với sơ đồ đối tượng và nó sẽ thực hiện liên kết chính xác. Giống

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

mà không yêu cầu phân tích $. Xem fiddle của tôi (nhật ký giao diện điều khiển) http://jsfiddle.net/k7czc/2/

Cập nhật : Có một ví dụ nhỏ về điều này trong tài liệu :

& hoặc & attr - cung cấp một cách để thực hiện một biểu thức trong ngữ cảnh của phạm vi cha. Nếu không có tên attr được chỉ định thì tên thuộc tính được giả sử giống với tên cục bộ. Định nghĩa phạm vi đã cho và widget của phạm vi: {localFn: '& myAttr'}, sau đó cô lập thuộc tính phạm vi localFn sẽ trỏ đến một trình bao bọc hàm cho biểu thức Count = Count + value. Thông thường, mong muốn truyền dữ liệu từ phạm vi bị cô lập thông qua một biểu thức và đến phạm vi cha, điều này có thể được thực hiện bằng cách chuyển một bản đồ các tên và giá trị của biến cục bộ vào trình bao bọc biểu thức fn. Ví dụ: nếu biểu thức là tăng (số tiền) thì chúng ta có thể chỉ định giá trị số tiền bằng cách gọi localFn là localFn ({lượng: 22}).


4
Rất đẹp! Đây có phải là tài liệu ở bất cứ đâu?
ach

12
Tôi không nghĩ đó là một giải pháp tốt bởi vì trong định nghĩa chỉ thị, đôi khi bạn sẽ không biết thông số nào cần truyền vào.
OMGPOP

Đây là một giải pháp tốt và cảm ơn bạn vì điều đó, nhưng tôi tin rằng câu trả lời cần một chút thủy triều. Lex82 là ai và anh ấy đã đề cập đến điều gì?
Wtower

Cách tiếp cận thú vị. Mặc dù điều gì xảy ra khi bạn muốn cho phép bất kỳ chức năng nào có tham số BẤT K ((hoặc nhiều) được thông qua? Bạn không biết gì về chức năng cũng như các tham số của nó và cần thực thi nó trong một số sự kiện bên trong chỉ thị. Làm thế nào để đi về nó? Ví dụ: trong một lệnh bạn có thể có onchangefunc = 'myCtrlFunc (DynamicVariableHere)'
trainoocation

58

Không có gì sai với các câu trả lời khác, nhưng tôi sử dụng kỹ thuật sau đây khi truyền các hàm trong một thuộc tính chỉ thị.

Bỏ dấu ngoặc đơn khi bao gồm lệnh trong html của bạn:

<my-directive callback="someFunction" />

Sau đó "mở khóa" chức năng trong liên kết hoặc bộ điều khiển của lệnh của bạn. đây là một ví dụ:

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
                });
            });
        }
    }
}]);    

Bước "hủy ghép" cho phép hàm được gọi bằng cú pháp tự nhiên hơn. Nó cũng đảm bảo rằng lệnh hoạt động đúng ngay cả khi được lồng trong các lệnh khác có thể vượt qua hàm. Nếu bạn không thực hiện việc hủy ghép nối, thì nếu bạn có một kịch bản như thế này:

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

Sau đó, bạn sẽ kết thúc với một cái gì đó như thế này trong chỉ thị nội tâm của bạn:

callback()()()(data); 

Mà sẽ thất bại trong các kịch bản lồng nhau khác.

Tôi đã điều chỉnh kỹ thuật này từ một bài viết xuất sắc của Dan Wahlin tại http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-feft-parameter

Tôi đã thêm bước hủy ghép nối để làm cho việc gọi hàm trở nên tự nhiên hơn và để giải quyết vấn đề lồng mà tôi gặp phải trong một dự án.


2
Một cách tiếp cận đẹp nhưng tôi không thể sử dụng thiscon trỏ bên trong phương thức gọi lại, vì nó sử dụng phạm vi của lệnh. Tôi đang sử dụng Bản mô tả và cuộc gọi lại của tôi trông như thế này: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
Lưu ý: nếu bạn có các lệnh lồng nhau và muốn truyền lệnh gọi lại lên trên, bạn cần mở khóa trong mỗi lệnh, không chỉ một lệnh gọi kích hoạt.
Episodex

43

Trong chỉ thị ( myDirective):

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

Trong mẫu chỉ thị:

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

Trong nguồn:

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

... Nơi myFunctionđược định nghĩa trong bộ điều khiển.

Lưu ý rằng paramtrong mẫu chỉ thị liên kết gọn gàng paramtrong nguồn và được đặt thành item.


Để gọi từ bên trong thuộc linktính của một lệnh ("bên trong" của nó), sử dụng một cách tiếp cận rất giống nhau:

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

Trong khi có Trong nguồn: ràng buộc-hàm = 'myFactor (obj1.param, obj2.param)'> thì làm thế nào để tiến hành?
Ankit Pandey

15

Có, có một cách tốt hơn: Bạn có thể sử dụng dịch vụ $ parse trong lệnh của bạn để đánh giá một biểu thức trong ngữ cảnh của phạm vi cha mẹ trong khi ràng buộc một số định danh nhất định trong biểu thức với các giá trị chỉ hiển thị trong lệnh của bạn:

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

Thêm dòng này vào chức năng liên kết của chỉ thị nơi bạn có thể truy cập các thuộc tính của lệnh.

Thuộc tính gọi lại của bạn sau đó có thể được đặt như thế callback = "callback(item.id, arg2)"vì arg2 bị ràng buộc với yourSecondArgument bởi dịch vụ $ parse bên trong lệnh. Các chỉ thị như ng-clickcho phép bạn truy cập sự kiện nhấp qua$event định danh bên trong biểu thức được truyền cho chỉ thị bằng cách sử dụng chính xác cơ chế này.

Lưu ý rằng bạn không phải tạo callbackthành viên trong phạm vi biệt lập của mình với giải pháp này.


3
Việc sử dụng scope.$parentlàm cho lệnh "rò rỉ" - nó "biết" quá nhiều thế giới bên ngoài, một thành phần được đóng gói được thiết kế tốt không nên.
Dmitri Zaitsev

3
Chà, nó biết rằng nó có phạm vi cha mẹ nhưng nó không truy cập vào một trường cụ thể trong phạm vi đó nên tôi nghĩ điều này có thể chấp nhận được.
lex82

0

Đối với tôi sau đây làm việc:

trong chỉ thị tuyên bố như thế này:

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

Trong mẫu chỉ thị sử dụng nó theo cách sau:

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

Và sau đó khi bạn sử dụng lệnh, hãy truyền hàm như thế này:

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

Bạn truyền hàm bằng cách khai báo của nó và nó được gọi từ chỉ thị và các tham số được điền.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.