2015-04-21 45 views
7

Tôi có 3 lồng nhau, cô lập phạm vi, chỉ thị (see CodePen here) và tôi có thể chuyển một hàm (lấy một đối số) từ chỉ thị ngoài cùng đến chỉ thị bên trong nhất từ chỉ thị ngoài đến chỉ thị trung gian tới chỉ thị bên trong).Chuyển các đối số hàm qua nhiều phạm vi biệt lập với các chỉ thị góc

Những gì tôi không hiểu là những gì cần phải được thực hiện để chuyển đối số từ chỉ thị bên trong trở lại thông qua chỉ thị trung gian trở lại chỉ thị bên ngoài.

Một lần nữa, hãy xem the CodePen example.

Lưu ý: Do chỉ có 2 cô lập phạm vi chỉ thị tôi có thể có được điều này để làm việc với một cái gì đó tương tự như sau ...

angular.module('myApp', []) 
    .directive('myDir1', function() { 
    return { 
     template: '<div><my-dir-2 add-name="addName(name)"></my-dir-2></div>', 
     controller: function($scope) { 
     $scope.addName = function(name) { 
      alert(name); // alerts 'Henry' 
     }; 
     } 
    } 
    }) 
    .directive('myDir2', function() { 
    return { 
     scope: { 
     addName: '&' 
     }, 
     template: "<span ng-click='addName({name: testName})'>Click to Add {{testName}}!</span>", 
     controller: function($scope) { 
     $scope.testName = 'Henry'; 
     } 
    } 
    }); 

Đoạn mã trên mang lại cho tôi một hộp cảnh báo với 'Henry' (giống như tôi mong đợi).

Đó là khi tôi thêm một, trung cấp, cô lập phạm vi chỉ thị thứ ba mà tôi chạy vào vấn đề ...

angular.module('myApp', []) 
    .directive('myDir1', function() { 
    return { 
     template: '<div><my-dir-2 add-name="addName(name)"></my-dir-2></div>', 
     controller: function($scope) { 
     $scope.addName = function(name) { 
      alert(name); // alerts 'Henry' 
     }; 
     } 
    } 
    }) 
    .directive('myDir2', function() { 
    return { 
     scope: { 
     addName: '&' 
     }, 
     template: '<div><my-dir-3 add-name="addName({name: testName})"></my-dir-3></div>', 
    } 
    }) 
    .directive('myDir3', function() { 
    return { 
     scope: { 
     addName: '&' 
     }, 
     template: "<span ng-click='addName({name: testName})'>Click to Add {{testName}}!</span>", 
     controller: function($scope) { 
     $scope.testName = 'Henry'; 
     } 
    } 
    }); 

Mã này mang lại cho tôi một hộp cảnh báo với undefined ...

Trả lời

17

Một quan niệm sai lầm phổ biến là "& là dành cho các chức năng truyền". Đây không phải là kỹ thuật chính xác.

Điều & làm là tạo một hàm trên phạm vi chỉ thị mà khi được gọi, trả về kết quả của biểu thức được đánh giá dựa vào phạm vi gốc.

Hàm này lấy đối tượng làm đối số sẽ ghi đè các biến cục bộ trong biểu thức với các đối tượng từ đối tượng phạm vi chỉ thị (đối tượng {name: testName}) trong trường hợp này.

Nếu bạn nhìn dưới mui xe, các $scope.addName phương pháp trong myDir2 sẽ trông như thế này (giản thể):

$scope.addName = function(locals) { 
    return $parse(attr.addName)($scope.$parent, locals); 
} 

chỉ thị thứ hai của bạn làm việc vì biểu thức nó được gắn vào là

addName(name) 

Biểu thức này có biến cục bộ name, được ghi đè bằng giá trị của testName từ chỉ thị khi được thực thi với

addName({name: testName}) //in the directive. 

Hãy nhớ - chức năng addName trong myDir2 KHÔNG giống như chức năng addName trong myDir1. Đây là hàm mới đánh giá biểu thức

addName(name) 

so với phạm vi gốc và trả về kết quả.

Khi bạn áp dụng logic này để myDir3, khái niệm đó được đánh giá là:

addName({name: testName}) 

Lưu ý rằng các biến cục bộ chỉ trong biểu thức này là "testName". Vì vậy, khi bạn gọi trong myDir3 với

addName({name: testName}) 

không có biến cục bộ name để ghi đè lên, và testName còn lại không xác định.

Phew! Không có thắc mắc điều này gây nhầm lẫn JUST VỀ EVERYBODY!

Làm thế nào để sửa chữa trong ví dụ của bạn:

Bạn muốn các biểu thức để đánh giá với chức năng thực tế trong myDir1.

angular.module('myApp', []) 
    .directive('myDir1', function() { 
    return { 
     template: '<div><my-dir-2 add-name="addName"></my-dir-2></div>', 
     controller: function($scope) { 
     $scope.addName = function(name) { 
      alert(name); // alerts 'Henry' 
     }; 
     } 
    } 
    }) 
    .directive('myDir2', function() { 
    return { 
     scope: { 
     addName: '&' 
     }, 
     // addName() returns the actual function addName from myDir1 
     template: '<div><my-dir-3 add-name="addName()"></my-dir-3></div>', 
    } 
    }) 
    .directive('myDir3', function() { 
    return { 
     scope: { 
     addName: '&' 
     }, 
     //calls addName() on myDir2, which returns addName from myDir1, then invokes it passing testName as an argument 
     template: "<span ng-click='addName()(testName)'>Click to Add {{testName}}!</span>", 
     controller: function($scope) { 
     $scope.testName = 'Henry'; 
     } 
    } 
    }); 

Here is the working Pen

Cuối cùng lưu ý - lý do tại sao '&' là thích hợp hơn '=' ở đây là '=' sẽ thực sự thiết lập một $watch và hai chiều ràng buộc các biến giữa các chỉ thị. Điều này có nghĩa là myDir2 thực sự có thể thay đổi chức năng appName trong myDir1, không bắt buộc và không mong muốn. Nó cũng yêu cầu thiết lập hai $watch s, mà tôi cố gắng tránh vì lý do hiệu suất trong Góc.

+0

câu trả lời tuyệt vời, thực sự học được điều gì đó ở đây! những gì tôi không hiểu: ở cấp cao nhất, bạn thường chuyển hàm bên trong khuôn mẫu thông qua 'add-name =" addName (name) "', như trong câu trả lời này: http://stackoverflow.com/a/ 18378602/3779853.Tại sao mức "vượt qua" cao nhất phải là 'add-name =" addName "' không có tham số, trong mẫu 'myDir1'? – Blauhirn

+0

@Blauhirn - nếu bạn cố chuyển nó thành 'add-name =" addName (name) "', hàm 'addName' sẽ thực thi ngay lập tức, và kết quả của hàm sẽ được truyền vào, chứ không phải tham chiếu đến hàm chức năng. – ps2goat

-1

Bạn có thể truy cập một yếu tố phạm vi sử dụng isolateScope()

Trong chỉ thị myDir1 thay thế controller để thay này

link: function($scope, elem) { 
    $scope.addName = function(name) { 
     var e = elem.find('my-dir-3'); 
     var s = e.isolateScope(); 
     alert(s.testName); 
    }; 

Một cách khác là khả năng sử dụng $$nextSibling để có được phạm vi tiếp theo sau khi cha mẹ hiện tại (chưa thực sự sử dụng này để có thể muốn đọc lên trên đó)

Đây có lẽ không phải là 'cách góc cạnh' để Làm vài thứ. Tôi nghĩ rằng việc loại bỏ phạm vi cô lập khỏi các chỉ thị của trẻ và để chúng tham chiếu đến mô hình chỉ thị cha mẹ sẽ lý tưởng hơn.

4

Có hai cách để chuyển một hàm trong một phạm vi riêng biệt. Trong khi '&' sẽ đảm bảo rằng những gì bạn đang truyền thực tế là một hàm, bạn cũng có thể chuyển một hàm dưới dạng biến bị ràng buộc bằng '=' và chỉ gọi nó khi bạn cần. Phương thức này có những hạn chế, nhưng nó sẽ để lại sự kiểm soát lời gọi đến thành phần phụ trách lời gọi đó.

Your codepen working

angular.module('myApp', []) 
.directive('myDir1', function() { 
    return { 
     template: '<div><my-dir-2 add-name="addName"></my-dir-2></div>', 
     controller: function($scope) { 
     $scope.addName = function(name) { 
     alert(name); 
     }; 
    } 
    } 
}) 
.directive('myDir2', function() { 
    return { 
    scope: { 
     addName: '=' 
    }, 
    template: '<div><my-dir-3 add-name="addName"></my-dir-3></div>' 
    } 
}) 
.directive('myDir3', function() { 
    return { 
    scope: { 
     addName: '=' 
    }, 
    template: "<span ng-click='addName(testName)'>Click to Add {{testName}}!</span>",  
    controller: function($scope) { 
     $scope.testName = "Henry" 
    } 
    } 
}); 
0

Vấn đề với mã của bạn với ba chỉ lồng nhau là testName không tồn tại trong phạm vi riêng biệt của myDir2. Bạn có thể thiết lập rằng từ myDir3, nhưng cho rằng bạn sẽ phải tạo một đối tượng trong phạm vi myDir2 và thiết lập tài sản của mình để testName và sau đó sử dụng tài sản đó trong myDir2

Ví dụ làm việc ở đây http://codepen.io/anon/pen/Wveqwx

Các vấn đề liên quan