webentwicklung-frage-antwort-db.com.de

Wie kann man das Verhalten einer Direktive mit isoliertem Geltungsbereich offenlegen?

Wie kann ich eine Methode aus einer Direktive herausstellen? Ich weiß, dass ich Attribute für Daten verwenden sollte, aber ich möchte wirklich Verhalten , nicht Daten verfügbar machen. Etwas, das der übergeordnete Controller aufrufen kann.

Angenommen, mein DOM sieht folgendermaßen aus:

<div ng-app="main">
    <div ng-controller="MyCtrl">
        <button ng-click="call()" >Call</button>
        <div id="container" my-directive> </div>
    </div>
</div>

JavaScript:

angular.module("main", []).controller("MyCtrl", function($scope) {
    $scope.call = function() {
        $scope.myfn();
    };
}).directive("myDirective", function() {
    return {
        // scope: {},
        controller: function($scope) {
            $scope.myfn = function() {
                console.log("myfn called");
            }
        }
    };
});

jsFiddle: http://jsfiddle.net/5gDjQ/7/

Wenn die scope auskommentiert ist (d. H. Die Direktive hat keinen isolierten Geltungsbereich), funktioniert sie einwandfrei. Wenn ich den Knopf drücke, wird myfn aufgerufen und meldet sich an der Konsole an.

Sobald ich scope auskommentiere, funktioniert es nicht mehr. myfn ist für den untergeordneten Bereich definiert und für den übergeordneten nicht leicht verfügbar.

In meinem Fall halte ich die Verschmutzung des übergeordneten Bereichs für eine schlechte Idee und möchte sie unbedingt vermeiden.

Wie kann ich also eine Funktion aus der Direktive für den übergeordneten Controller verfügbar machen? Oder: Wie kann ich eine Methode für Direktiven vom übergeordneten Controller aus aufrufen?

34
Konrad Garus

Sie können dies mit einem isolierten Bereich tun, indem Sie eine Variable in dem Bereich einrichten, die bidirektional an den Controller gebunden ist (mithilfe von '='). In Ihrer Direktive können Sie die Funktion dann dieser Variablen zuweisen, und angle wird die Bindung verwenden, um die entsprechende Variable in Ihrem Controller zu finden. Diese Variable zeigt auf eine Funktion, die Ihr Controller aufrufen kann.

http://jsfiddle.net/GWCCr/

html: Beachten Sie das neue Attribut:

<div ng-app="main">
    <div ng-controller="MyCtrl">
        <button ng-click="call()" >Call</button>
        <div id="container" my-directive my-fn="fnInCtrl"> </div>
    </div>
</div>

js:

angular.module("main", []).controller("MyCtrl", function($scope) {
    $scope.call = function() {
        $scope.fnInCtrl();
    };
}).directive("myDirective", function() {
    return {
        scope: {
            myFn: '='
        },
        controller: function($scope) {
            $scope.myFn = function() {
                console.log("myfn called");
            }
        }
    };
});
26
Roy Truelove

Anstatt zu versuchen herauszufinden, wie eine Funktion innerhalb einer Direktive versteckt aufgerufen werden kann, sollten Sie sich fragen: Warum möchte ich eine in einer Direktive definierte Funktion aufrufen?

Ein Grund, den ich mir vorstellen kann, ist: um ein Verhalten der Direktive auszulösen, das vom Benutzer der Anwendung auch innerhalb der Direktive ausgelöst werden könnte .

Wenn ja, ist das Offensichtliche und Angulary-Anliegen, ein Ereignis auszustrahlen in einem Bereich, der die Direktive enthält, die darauf reagieren sollte. Dann würde die Direktive auf dieses Ereignis hören und seine Funktion selbst auslösen. 

Dies hat zusätzliche Vorteile:

  • sie können Parameter im Ereignisdatenobjekt senden, wenn Sie möchten
  • sie können eine Reaktion in mehr als einer Direktive auslösen (z. B. wenn sie eine Sammlung bilden)
  • sie können mit einer Direktive in der Gültigkeitsbereichshierarchie kommunizieren (z. B. wenn sich die Direktive in einer anderen Direktive befindet, die sich in anderen Direktiven befindet usw.), ohne einen Funktionsrückruf durch jede geschachtelte Direktive zu übergeben
  • die Isolation der Richtlinie wird dadurch nicht gebrochen

Beispiel

Versuchen wir, ein sehr einfaches Beispiel zu finden: Angenommen, wir haben ein Widget, das ein beliebiges inspirierendes Zitat anzeigt, das von irgendwo heruntergeladen wurde. Es hat auch eine Schaltfläche, um das Angebot in ein anderes zu ändern.

Hier ist die Vorlage der Direktive:

<p>{{ quote }}</p>
<button ng-click="refreshQuote()"></button>

Und hier ist der Code der Direktive:

app.directive("randomQuote", function () {
  return {
    restrict: "E",
    scope: {},
    link: function (scope) {
      scope.refreshQuote = function () {
        scope.quote = ... // some complicated code here
      };
      scope.refreshQuote();
    }
  };
});

Beachten Sie, dass die Direktive vollständig in sich abgeschlossen ist: Sie hat einen isolierten Geltungsbereich und führt den Abruf von Anführungszeichen selbst aus.

Nehmen wir an, wir möchten auch das Angebot vom Controller aktualisieren können. Dies kann so einfach sein wie das Aufrufen im Controller-Code:

$scope.$broadcast("refresh-random-quote");

Um den Event-Handler hinzuzufügen, müssen wir diesen Code zur link-Funktion der Direktive hinzufügen:

scope.$on("refresh-random-quote", function () {
  scope.refreshQuote();
});

Auf diese Weise haben wir einen unidirektionalen Kommunikationskanal vom Controller zur Direktive geschaffen, der die Isolation der Direktive nicht durchbricht und auch funktioniert, wenn die Direktive tief in der Hierarchie des Codes verschachtelt ist, der das Ereignis sendet .

16
DzinX

Wie kann ich eine Funktion aus der Direktive für den übergeordneten Controller verfügbar machen?
Oder: Wie kann ich eine Methode per Direktive vom übergeordneten Controller aus aufrufen?

Nun, ich denke nicht, dass Sie dies versuchen sollten (dh, das Verhalten des Controllers an eine Direktive zu koppeln), aber wenn Sie müssen ... Hier haben Sie eine Möglichkeit, dies zu tun: Übergeben Sie eine Controllerfunktion an Ihre Direktive, die die Direktive kann den Controller über die Direktive-Funktion benachrichtigen:

<div id="container" my-directive cb="setDirectiveFn(fn)"></div>

directive("myDirective", function() {
    return {
       scope: { cb: '&' },
        controller: function($scope) {
            $scope.myfn = function() {
                console.log("myfn called");
            }
            $scope.cb({fn: $scope.myfn});
        }
    };
});

Fiddle

7
Mark Rajcok

Als Beitrag leistete @georgeawg mir eine coole Lösung, die den Dienst mit Service erledigte. Auf diese Weise können Sie mehrere Anweisungen auf derselben Seite verwalten.

<html ng-app="myApp">
<head>
  <script src="https://opensource.keycdn.com/angularjs/1.6.5/angular.min.js"></script>
</head>
<body ng-controller="mainCtrl">
  <h1>^v1.6.0 ($postLink hook required)</h1>
  <my-directive name="sample1" number="number1"></my-directive>
  <my-directive name="sample2" number="number2"></my-directive>
</body>
<script>
  angular.module('myApp', [])
    .controller('mainCtrl', ['$scope', 'myDirectiveFactory', function ($scope, myDirectiveFactory) {
      $scope.number1 = 10
      $scope.number2 = 0
      this.$postLink = function () {
        myDirectiveFactory.get('sample2')
          .increment()
          .increment()
          .increment()
          .increment()
        myDirectiveFactory.get('sample1')
          .increment()
          .increment()
        myDirectiveFactory.get('sample2')
        .decrement()
      }
    }])
    .factory('myDirectiveFactory', function () {
      var instance = {}
      return {
        get: function (name) {
          return instance[name]
        },
        register: function (name, value) {
          return instance[name] = value
        },
        destroy: function (name) {
          delete instance[name]
        }
      }
    })
    .controller('myDirectiveCtrl', ['$scope', 'myDirectiveFactory', function ($scope, myDirectiveFactory) {
      $scope.name = $scope.name || 'myDirective'
      $scope.$on('$destroy', function () {
        myDirectiveFactory.destroy($scope.name)
      })
      var service = {
        increment: function () {
          $scope.number++
          return this
        },
        decrement: function () {
          $scope.number--
          return this
        }
      }
      myDirectiveFactory.register($scope.name, service)
    }])
    .directive('myDirective', [function () {
      return {
        controller: 'myDirectiveCtrl',
        restrict: 'E',
        scope: {
          number: '<',
          name: '@?'
        },
        template: '<p> {{ number }} </p>'
      }
    }])
</script>
</html>
1
saulsluz

Die Veröffentlichung von AngularJS V1.7.1* führt die neue ng-ref Direktive ein.

Das Attribut ng-ref weist AngularJS an, den Controller einer Komponente im aktuellen Bereich zu veröffentlichen. Dies ist nützlich, wenn eine Komponente, z. B. ein Audioplayer, seine API für gleichgeordnete Komponenten verfügbar macht. Auf die Spiel- und Stopp-Bedienelemente kann leicht zugegriffen werden.

Weitere Informationen finden Sie unter

0
georgeawg