Le moyen le plus simple de passer une variable de portée AngularJS d'une directive à un contrôleur?


99

Quelle est la manière la plus simple de passer une variable de portée AngularJS d'une directive à un contrôleur? Tous les exemples que j'ai vus semblent si complexes, n'y a-t-il pas un moyen d'accéder à un contrôleur à partir d'une directive et de définir l'une de ses variables de portée?


voir stackoverflow.com/questions/17900201/… pour plus d'informations
— Saksham

Réponses:


150

Édité le 25/08/2014: c'est ici que je l'ai fourché.

Merci @anvarik.

Voici le jsFiddle . J'ai oublié où j'ai fourché ça. Mais ceci est un bon exemple montrant la différence entre = et @

<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
Excellente explication et exemple! Je me demande pourquoi la documentation est si complexe? ... Ou est-ce que je ne suis pas un si bon programmeur?
— kshep92

2
Notez que ce violon fonctionne comme dans, mais si vous changez la version angulaire pour une version plus récente (c'est-à-dire de 1.0.1 à 1.2.1), cela ne fonctionnera plus. Quelque chose a dû changer dans la syntaxe.
— eremzeit le

2
Enfin un exemple clair qui a du sens. Mal de tête de 2 heures résolu en 10 secondes.
— Chris

4
Comment se fait-il que tout le monde vote cette réponse alors que la méthode explique comment passer une valeur d'un contrôleur à une directive et non d'une directive à un contrôleur?
— Tiberiu C.

2
isolatedBindingFoo: '= bindingFoo' peut transmettre les données de la directive au contrôleur. ou vous pouvez utiliser le service. Avant de voter pour quelqu'un, vous pouvez le demander d'abord si vous ne comprenez pas.
— maxisam

70

Attendez que angular ait évalué la variable

J'ai eu beaucoup de tripotages avec cela, et je n'ai pas pu le faire fonctionner même avec la variable définie avec "="dans la portée. Voici trois solutions en fonction de votre situation.


Solution n ° 1


J'ai trouvé que la variable n'était pas encore évaluée par angulaire lorsqu'elle a été transmise à la directive. Cela signifie que vous pouvez y accéder et l'utiliser dans le modèle, mais pas à l'intérieur du lien ou de la fonction de contrôleur d'application, sauf si nous attendons qu'il soit évalué.

Si votre variable change ou est récupérée via une requête, vous devez utiliser $observeou $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);
        }
    });
}]);

Et voici le html (rappelez-vous les crochets!):

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

Notez que vous ne devez pas définir la variable "="dans la portée, si vous utilisez la $observefonction. De plus, j'ai trouvé qu'il transmettait les objets sous forme de chaînes, donc si vous passez des objets, utilisez la solution n ° 2 ou scope.$watch(attrs.yourDirective, fn)(, ou n ° 3 si votre variable ne change pas).


Solution n ° 2


Si votre variable est créée dans un autre contrôleur , par exemple , mais que vous avez juste besoin d'attendre que angular l'ait évaluée avant de l'envoyer au contrôleur d'application, nous pouvons utiliser $timeoutpour attendre que le $applysoit exécuté. Nous devons également l'utiliser $emitpour l'envoyer au contrôleur d'application de la portée parent (en raison de la portée isolée dans la directive):

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

Et voici le html (sans crochets!):

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

Solution n ° 3


Si votre variable ne change pas et que vous devez l'évaluer dans votre directive, vous pouvez utiliser la $evalfonction:

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

Et voici le html (rappelez-vous les crochets!):

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

Jetez également un œil à cette réponse: https://stackoverflow.com/a/12372494/1008519

Référence pour le problème FOUC (flash of unstyled content): http://deansofer.com/posts/view/14/AngularJs-Tips-and-Tricks-UPDATED

Pour les intéressés: voici un article sur le cycle de vie angulaire


1
Parfois, un simple ng-if="someObject.someVariable"sur la directive (ou l'élément avec la directive comme attribut) est suffisant - la directive entre en action seulement après avoir someObject.someVariableété définie.
— marapet
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.