Cách dễ nhất để chuyển một biến phạm vi AngularJS từ chỉ thị đến bộ điều khiển?


99

Cách dễ nhất để chuyển một biến phạm vi AngularJS từ chỉ thị đến bộ điều khiển là gì? Tất cả các ví dụ mà tôi đã thấy có vẻ rất phức tạp, không có cách nào để tôi có thể truy cập bộ điều khiển từ một chỉ thị và đặt một trong các biến phạm vi của nó?


xem stackoverflow.com/questions/17900201/… để biết thêm thông tin chi tiết
Saksham,

Câu trả lời:


150

Đã chỉnh sửa vào ngày 25 tháng 8 năm 2014: Đây là nơi tôi đã chia nó.

Cảm ơn @anvarik.

Đây là JSFiddle . Tôi quên nơi tôi đã đặt cái này. Nhưng đây là một ví dụ điển hình cho bạn thấy sự khác biệt giữa = và @

<div ng-controller="MyCtrl">
    <h2>Parent Scope</h2>
    <input ng-model="foo"> <i>// Update to see how parent scope interacts with component scope</i>    
    <br><br>
    <!-- attribute-foo binds to a DOM attribute which is always
    a string. That is why we are wrapping it in curly braces so
    that it can be interpolated. -->
    <my-component attribute-foo="{{foo}}" binding-foo="foo"
        isolated-expression-foo="updateFoo(newFoo)" >
        <h2>Attribute</h2>
        <div>
            <strong>get:</strong> {{isolatedAttributeFoo}}
        </div>
        <div>
            <strong>set:</strong> <input ng-model="isolatedAttributeFoo">
            <i>// This does not update the parent scope.</i>
        </div>
        <h2>Binding</h2>
        <div>
            <strong>get:</strong> {{isolatedBindingFoo}}
        </div>
        <div>
            <strong>set:</strong> <input ng-model="isolatedBindingFoo">
            <i>// This does update the parent scope.</i>
        </div>
        <h2>Expression</h2>    
        <div>
            <input ng-model="isolatedFoo">
            <button class="btn" ng-click="isolatedExpressionFoo({newFoo:isolatedFoo})">Submit</button>
            <i>// And this calls a function on the parent scope.</i>
        </div>
    </my-component>
</div>
var myModule = angular.module('myModule', [])
    .directive('myComponent', function () {
        return {
            restrict:'E',
            scope:{
                /* NOTE: Normally I would set my attributes and bindings
                to be the same name but I wanted to delineate between
                parent and isolated scope. */                
                isolatedAttributeFoo:'@attributeFoo',
                isolatedBindingFoo:'=bindingFoo',
                isolatedExpressionFoo:'&'
            }        
        };
    })
    .controller('MyCtrl', ['$scope', function ($scope) {
        $scope.foo = 'Hello!';
        $scope.updateFoo = function (newFoo) {
            $scope.foo = newFoo;
        }
    }]);

29
Giải thích và ví dụ tuyệt vời! Tôi tự hỏi tại sao tài liệu lại phức tạp như vậy? ... Hay là tôi không phải là một lập trình viên giỏi đến vậy?
kshep92 Ngày

2
Lưu ý rằng fiddle này hoạt động như trong, nhưng nếu bạn thay đổi phiên bản góc cạnh thành một phiên bản mới hơn (tức là từ 1.0.1 sang 1.2.1), nó sẽ không hoạt động nữa. Chắc hẳn có gì đó đã thay đổi về cú pháp.
ermzeit

2
Cuối cùng là một ví dụ rõ ràng có ý nghĩa. Đau đầu 2hr được giải quyết trong 10 giây.
Chris

4
Tại sao mọi người lại bỏ phiếu cho câu trả lời này trong khi phương pháp giải thích cách chuyển một giá trị từ bộ điều khiển đến một chỉ thị chứ không phải từ một chỉ thị tới bộ điều khiển?
Tiberiu C.

2
BindingFoo: '= bindingFoo' có thể chuyển dữ liệu từ chỉ thị đến bộ điều khiển. hoặc bạn có thể sử dụng dịch vụ. Trước khi bỏ phiếu cho ai đó, bạn có thể hỏi trước nếu bạn không hiểu.
maxisam

70

Chờ cho đến khi góc đã đánh giá biến

Tôi đã phải loay hoay với điều này và không thể làm cho nó hoạt động ngay cả với biến được xác định "="trong phạm vi. Đây là ba giải pháp tùy thuộc vào tình huống của bạn.


Giải pháp # 1


Tôi nhận thấy rằng biến chưa được đánh giá bằng góc khi nó được chuyển tới chỉ thị. Điều này có nghĩa là bạn có thể truy cập nó và sử dụng nó trong mẫu, nhưng không phải bên trong liên kết hoặc chức năng bộ điều khiển ứng dụng trừ khi chúng tôi đợi nó được đánh giá.

Nếu biến của bạn đang thay đổi hoặc được tìm nạp thông qua một yêu cầu, bạn nên sử dụng $observehoặc $watch:

app.directive('yourDirective', function () {
    return {
        restrict: 'A',
        // NB: no isolated scope!!
        link: function (scope, element, attrs) {
            // observe changes in attribute - could also be scope.$watch
            attrs.$observe('yourDirective', function (value) {
                if (value) {
                    console.log(value);
                    // pass value to app controller
                    scope.variable = value;
                }
            });
        },
        // the variable is available in directive controller,
        // and can be fetched as done in link function
        controller: ['$scope', '$element', '$attrs',
            function ($scope, $element, $attrs) {
                // observe changes in attribute - could also be scope.$watch
                $attrs.$observe('yourDirective', function (value) {
                    if (value) {
                        console.log(value);
                        // pass value to app controller
                        $scope.variable = value;
                    }
                });
            }
        ]
    };
})
.controller('MyCtrl', ['$scope', function ($scope) {
    // variable passed to app controller
    $scope.$watch('variable', function (value) {
        if (value) {
            console.log(value);
        }
    });
}]);

Và đây là html (hãy nhớ dấu ngoặc!):

<div ng-controller="MyCtrl">
    <div your-directive="{{ someObject.someVariable }}"></div>
    <!-- use ng-bind in stead of {{ }}, when you can to avoids FOUC -->
    <div ng-bind="variable"></div>
</div>

Lưu ý rằng bạn không nên đặt biến "="trong phạm vi, nếu bạn đang sử dụng $observehàm. Ngoài ra, tôi thấy rằng nó truyền các đối tượng dưới dạng chuỗi, vì vậy nếu bạn đang chuyển các đối tượng, hãy sử dụng giải pháp # 2 hoặc scope.$watch(attrs.yourDirective, fn)(hoặc # 3 nếu biến của bạn không thay đổi).


Giải pháp # 2


Nếu biến của bạn được tạo trong một bộ điều khiển khác , chẳng hạn như một bộ điều khiển khác , nhưng chỉ cần đợi cho đến khi góc đánh giá nó trước khi gửi nó đến bộ điều khiển ứng dụng, chúng ta có thể sử dụng $timeoutđể đợi cho đến khi $applynó chạy. Ngoài ra, chúng tôi cần sử dụng $emitđể gửi nó đến bộ điều khiển ứng dụng phạm vi mẹ (do phạm vi bị cô lập trong chỉ thị):

app.directive('yourDirective', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        // NB: isolated scope!!
        scope: {
            yourDirective: '='
        },
        link: function (scope, element, attrs) {
            // wait until after $apply
            $timeout(function(){
                console.log(scope.yourDirective);
                // use scope.$emit to pass it to controller
                scope.$emit('notification', scope.yourDirective);
            });
        },
        // the variable is available in directive controller,
        // and can be fetched as done in link function
        controller: [ '$scope', function ($scope) {
            // wait until after $apply
            $timeout(function(){
                console.log($scope.yourDirective);
                // use $scope.$emit to pass it to controller
                $scope.$emit('notification', scope.yourDirective);
            });
        }]
    };
}])
.controller('MyCtrl', ['$scope', function ($scope) {
    // variable passed to app controller
    $scope.$on('notification', function (evt, value) {
        console.log(value);
        $scope.variable = value;
    });
}]);

Và đây là html (không có dấu ngoặc!):

<div ng-controller="MyCtrl">
    <div your-directive="someObject.someVariable"></div>
    <!-- use ng-bind in stead of {{ }}, when you can to avoids FOUC -->
    <div ng-bind="variable"></div>
</div>

Giải pháp # 3


Nếu biến của bạn không thay đổi và bạn cần đánh giá nó trong chỉ thị của mình, bạn có thể sử dụng $evalhàm:

app.directive('yourDirective', function () {
    return {
        restrict: 'A',
        // NB: no isolated scope!!
        link: function (scope, element, attrs) {
            // executes the expression on the current scope returning the result
            // and adds it to the scope
            scope.variable = scope.$eval(attrs.yourDirective);
            console.log(scope.variable);

        },
        // the variable is available in directive controller,
        // and can be fetched as done in link function
        controller: ['$scope', '$element', '$attrs',
            function ($scope, $element, $attrs) {
                // executes the expression on the current scope returning the result
                // and adds it to the scope
                scope.variable = scope.$eval($attrs.yourDirective);
                console.log($scope.variable);
            }
         ]
    };
})
.controller('MyCtrl', ['$scope', function ($scope) {
    // variable passed to app controller
    $scope.$watch('variable', function (value) {
        if (value) {
            console.log(value);
        }
    });
}]);

Và đây là html (hãy nhớ dấu ngoặc!):

<div ng-controller="MyCtrl">
    <div your-directive="{{ someObject.someVariable }}"></div>
    <!-- use ng-bind instead of {{ }}, when you can to avoids FOUC -->
    <div ng-bind="variable"></div>
</div>

Ngoài ra, hãy xem câu trả lời này: https://stackoverflow.com/a/12372494/1008519

Tham khảo cho vấn đề FOUC (nội dung chưa được định kiểu): http://deansofer.com/posts/view/14/AngularJs-Tips-and-Tricks-UPDATED

Đối với những người quan tâm: đây là một bài viết về vòng đời góc


1
Đôi khi một ng-if="someObject.someVariable"chỉ thị đơn giản (hoặc phần tử có chỉ thị làm thuộc tính) là đủ - chỉ thị chỉ có hiệu lực sau khi someObject.someVariableđược xác định.
marapet
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.