Cara termudah untuk melewatkan variabel lingkup AngularJS dari direktif ke pengontrol?


Jawaban:


150

Diedit pada 2014/8/25: Di sinilah saya mem-forknya .

Terima kasih @anvarik.

Inilah JSFiddle . Saya lupa di mana saya bercabang ini. Tapi ini adalah contoh bagus yang menunjukkan perbedaan antara = dan @

<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
Penjelasan dan contoh yang bagus! Saya bertanya-tanya mengapa dokumentasinya begitu rumit? ... Atau apakah saya bukan programmer yang hebat?
kshep92

2
Perhatikan bahwa biola ini berfungsi seperti pada, tetapi jika Anda mengubah versi sudut ke versi yang lebih baru (yaitu dari 1.0.1 ke 1.2.1), biola tidak akan berfungsi lagi. Sesuatu pasti telah berubah tentang sintaks.
eremzeit

2
Akhirnya contoh jelas yang masuk akal. Sakit kepala 2 jam teratasi dalam 10 detik.
Chris

4
Kenapa semua orang memilih jawaban ini sementara metode menjelaskan cara meneruskan nilai dari controller ke directive dan bukan dari directive ke controller?
Tiberiu C.

2
terisolasiBindingFoo: '= bindingFoo' dapat meneruskan data dari direktif ke pengontrol. atau Anda dapat menggunakan layanan. Sebelum Anda menjatuhkan suara seseorang, Anda dipersilakan untuk bertanya terlebih dahulu jika Anda tidak mengerti.
maxisam

70

Tunggu sampai angular mengevaluasi variabel

Saya telah banyak mengotak-atik ini, dan tidak bisa membuatnya bekerja bahkan dengan variabel yang ditentukan "="dalam ruang lingkup. Berikut tiga solusi tergantung pada situasi Anda.


Solusi # 1


Saya menemukan bahwa variabel belum dievaluasi oleh angular ketika diteruskan ke direktif. Ini berarti Anda dapat mengakses dan menggunakannya di template, tetapi tidak di dalam link atau fungsi pengontrol aplikasi kecuali kita menunggu untuk dievaluasi.

Jika variabel Anda berubah , atau diambil melalui permintaan, Anda harus menggunakan $observeatau $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);
        }
    });
}]);

Dan inilah htmlnya (ingat tanda kurung!):

<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>

Perhatikan bahwa Anda tidak boleh menyetel variabel ke "="dalam cakupan, jika Anda menggunakan $observefungsi tersebut. Juga, saya menemukan bahwa ia melewatkan objek sebagai string, jadi jika Anda mengirimkan objek gunakan solusi # 2 atau scope.$watch(attrs.yourDirective, fn)(, atau # 3 jika variabel Anda tidak berubah).


Solusi # 2


Jika variabel Anda dibuat di misalnya pengontrol lain , tetapi hanya perlu menunggu sampai angular telah mengevaluasinya sebelum mengirimkannya ke pengontrol aplikasi, kita dapat menggunakan $timeoutuntuk menunggu hingga $applytelah dijalankan. Juga kita perlu menggunakan $emituntuk mengirimkannya ke pengontrol aplikasi lingkup induk (karena lingkup terisolasi dalam direktif):

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

Dan inilah htmlnya (tanpa tanda kurung!):

<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>

Solusi # 3


Jika variabel Anda tidak berubah dan Anda perlu mengevaluasinya dalam direktif Anda, Anda dapat menggunakan $evalfungsi:

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

Dan inilah htmlnya (ingat tanda kurung!):

<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>

Juga, lihat jawaban ini: https://stackoverflow.com/a/12372494/1008519

Referensi untuk masalah FOUC (flash konten tanpa gaya): http://deansofer.com/posts/view/14/AngularJs-Tips-and-Tricks-UPDATED

Untuk yang tertarik: inilah artikel tentang siklus hidup sudut


1
Kadang-kadang ng-if="someObject.someVariable"direktif sederhana (atau elemen dengan direktif sebagai atribut) sudah cukup - direktif berlaku hanya setelah someObject.someVariableditentukan.
marapet
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.