Der einfachste Weg, eine AngularJS-Bereichsvariable von der Direktive an den Controller zu übergeben?

99

Was ist der einfachste Weg, eine AngularJS-Bereichsvariable von der Direktive an den Controller zu übergeben? Alle Beispiele, die ich gesehen habe, scheinen so komplex zu sein. Gibt es nicht eine Möglichkeit, über eine Direktive auf einen Controller zuzugreifen und eine seiner Bereichsvariablen festzulegen?

Aufzieh Spielzeug
quelle
siehe stackoverflow.com/questions/17900201/... für mehr Einsicht
saksham

Antworten:

150

Bearbeitet am 25.08.2014: Hier habe ich es gegabelt.

Danke @anvarik.

Hier ist die JSFiddle . Ich habe vergessen, wo ich das gegabelt habe. Dies ist jedoch ein gutes Beispiel, das Ihnen den Unterschied zwischen = und @ zeigt

<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;
        }
    }]);
Maxisam
quelle
29
Tolle Erklärung und Beispiel! Ich frage mich, warum die Dokumentation so komplex ist? ... Oder bin ich kein so großartiger Programmierer?
kshep92
2
Beachten Sie, dass diese Geige wie in funktioniert, aber wenn Sie die Winkelversion in eine neuere ändern (dh von 1.0.1 auf 1.2.1), funktioniert sie nicht mehr. An der Syntax muss sich etwas geändert haben.
Eremzeit
2
Endlich ein klares Beispiel, das Sinn macht. 2 Stunden Kopfschmerzen in 10 Sekunden gelöst.
Chris
4
Wie kommt es, dass alle diese Antwort abstimmen, während die Methode erklärt, wie ein Wert vom Controller an eine Direktive und nicht von einer Direktive an einen Controller übergeben wird?
Tiberiu C.
2
isolationBindingFoo: '= bindingFoo' kann die Daten von der Direktive an den Controller übergeben. oder Sie können Service nutzen. Bevor Sie jemanden abstimmen, können Sie ihn gerne zuerst fragen, wenn Sie ihn nicht verstehen.
Maxisam
70

Warten Sie, bis Angular die Variable ausgewertet hat

Ich habe viel damit herumgespielt und konnte es auch mit der im Bereich definierten Variablen nicht "="zum Laufen bringen. Hier sind drei Lösungen, abhängig von Ihrer Situation.


Lösung Nr. 1


Ich fand heraus, dass die Variable noch nicht durch Winkel bewertet wurde, als sie an die Direktive übergeben wurde. Dies bedeutet, dass Sie darauf zugreifen und es in der Vorlage verwenden können, jedoch nicht innerhalb der Link- oder App-Controller-Funktion, es sei denn, wir warten auf die Auswertung.

Wenn Ihre Variable zu ändern ist , oder abgerufen wird durch eine Anfrage, die Sie verwenden sollten $observeoder $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);
        }
    });
}]);

Und hier ist das HTML (denken Sie an die Klammern!):

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

Beachten Sie, dass Sie die Variable nicht im Bereich festlegen sollten "=", wenn Sie die $observeFunktion verwenden. Außerdem habe ich festgestellt, dass Objekte als Zeichenfolgen übergeben werden. Wenn Sie also Objekte übergeben, verwenden Sie Lösung 2 oder scope.$watch(attrs.yourDirective, fn)(oder 3, wenn sich Ihre Variable nicht ändert).


Lösung Nr. 2


Wenn Ihre Variable beispielsweise in einem anderen Controller erstellt wurde , Sie jedoch nur warten müssen, bis Angular sie ausgewertet hat, bevor Sie sie an den App-Controller senden, können Sie $timeoutwarten, bis der $applyausgeführt wird. Außerdem müssen wir es verwenden $emit, um es an den übergeordneten Bereichs-App-Controller zu senden (aufgrund des isolierten Bereichs in der Direktive):

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

Und hier ist das HTML (keine Klammern!):

<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ösung Nr. 3


Wenn sich Ihre Variable nicht ändert und Sie sie in Ihrer Direktive auswerten müssen, können Sie die folgende $evalFunktion verwenden:

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

Und hier ist das HTML (denken Sie an die Klammern!):

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

Schauen Sie sich auch diese Antwort an: https://stackoverflow.com/a/12372494/1008519

Referenz für das FOUC-Problem (Flash of unstyled content): http://deansofer.com/posts/view/14/AngularJs-Tips-and-Tricks-UPDATED

Für Interessierte: Hier ist ein Artikel über den Winkellebenszyklus

mlunoe
quelle
1
Manchmal reicht eine einfache ng-if="someObject.someVariable"Anweisung (oder das Element mit der Anweisung als Attribut) aus - die Anweisung wird erst aktiviert, nachdem sie someObject.someVariabledefiniert wurde.
Marapet