web-dev-qa-db-fra.com

Le moyen le plus simple de passer une variable d’étendue AngularJS de directive à contrôleur?

Quel est le moyen le plus simple de faire passer une variable d’étendue AngularJS d’une directive à l’automate? Tous les exemples que j'ai vus semblent si complexes, n'y a-t-il pas moyen de pouvoir accéder à un contrôleur à partir d'une directive et de définir l'une de ses variables de portée?

98
winduptoy

Édité le 25/08/2014: Here était où je l'ai fourchu.

Merci @Anvarik.

Voici le JSFiddle . J'ai oublié où j'ai fourré ça. Mais c’est un bon exemple qui montre 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;
        }
    }]);
150
maxisam

Attendez que angular ait évalué la variable

J'avais beaucoup à faire avec cela, et je ne pouvais pas 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'avait pas encore été évaluée par angular 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 de la fonction de liaison ou du contrôleur d'application, sauf si nous attendons son évaluation.

Si votre variable est en train de changer ou est extraite via une demande, vous devez utiliser $observe ou $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 code HTML (n'oubliez pas 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 sur _"="_ dans l'étendue, si vous utilisez la fonction $observe . En outre, j’ai trouvé qu’il transmettait les objets sous forme de chaînes. Ainsi, si vous transmettez des objets, utilisez la solution n ° 2 ou scope.$watch(attrs.yourDirective, fn) (, ou # 3 si votre variable ne change pas).


Solution n ° 2


Si votre variable est créée dans, par exemple, un autre contrôleur , mais il suffit d’attendre que angular l’ait évalué avant de l’envoyer au contrôleur de l’app, nous pouvons utiliser $timeout = attendre que le _$apply_ soit exécuté. Nous devons également utiliser $emit pour l’envoyer au contrôleur de l’application de portée parent (en raison de la portée isolée de 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 code HTML (pas de 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 le $eval une fonction:

_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 code HTML (n'oubliez pas 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>
_

Regardez également cette réponse: https://stackoverflow.com/a/12372494/1008519

Référence pour le numéro FOUC (flash de contenu non stylé): http://deansofer.com/posts/view/14/AngularJs-Tips-and-Tricks-UPDATED

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

70
mlunoe