it-swarm.com.de

Wie rufe ich eine in einer AngularJS-Direktive definierte Methode auf?

Ich habe eine Direktive, hier ist der Code: 

.directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {

            var center = new google.maps.LatLng(50.1, 14.4); 
            $scope.map_options = {
                zoom: 14,
                center: center,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            // create map
            var map = new google.maps.Map(document.getElementById(attrs.id), $scope.map_options);
            var dirService= new google.maps.DirectionsService();
            var dirRenderer= new google.maps.DirectionsRenderer()

            var showDirections = function(dirResult, dirStatus) {
                if (dirStatus != google.maps.DirectionsStatus.OK) {
                    alert('Directions failed: ' + dirStatus);
                    return;
                  }
                  // Show directions
                dirRenderer.setMap(map);
                //$scope.dirRenderer.setPanel(Demo.dirContainer);
                dirRenderer.setDirections(dirResult);
            };

            // Watch
            var updateMap = function(){
                dirService.route($scope.dirRequest, showDirections); 
            };    
            $scope.$watch('dirRequest.Origin', updateMap);

            google.maps.event.addListener(map, 'zoom_changed', function() {
                $scope.map_options.zoom = map.getZoom();
              });

            dirService.route($scope.dirRequest, showDirections);  
        }
    }
})

Ich möchte updateMap() für eine Benutzeraktion aufrufen. Die Aktionsschaltfläche befindet sich nicht in der Direktive. 

Was ist der beste Weg, um updateMap() von einem Controller aus aufzurufen?

288
mcbjam

Wenn Sie isolierte Bereiche verwenden möchten, können Sie ein Steuerobjekt mithilfe der bidirektionalen Bindung = einer Variablen aus dem Controller-Bereich übergeben. Sie können auch mehrere Instanzen derselben Anweisung auf einer Seite mit demselben Steuerobjekt steuern.

angular.module('directiveControlDemo', [])

.controller('MainCtrl', function($scope) {
  $scope.focusinControl = {};
})

.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{internalControl}}</div>',
    scope: {
      control: '='
    },
    link: function(scope, element, attrs) {
      scope.internalControl = scope.control || {};
      scope.internalControl.takenTablets = 0;
      scope.internalControl.takeTablet = function() {
        scope.internalControl.takenTablets += 1;
      }
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="directiveControlDemo">
  <div ng-controller="MainCtrl">
    <button ng-click="focusinControl.takeTablet()">Call directive function</button>
    <p>
      <b>In controller scope:</b>
      {{focusinControl}}
    </p>
    <p>
      <b>In directive scope:</b>
      <focusin control="focusinControl"></focusin>
    </p>
    <p>
      <b>Without control object:</b>
      <focusin></focusin>
    </p>
  </div>
</div>

356
Oliver Wienand

Angenommen, die Aktionsschaltfläche verwendet denselben Controller $scope wie die Direktive, definieren Sie einfach die Funktion updateMapin $scope innerhalb der Link-Funktion. Ihr Controller kann diese Funktion dann aufrufen, wenn Sie auf die Aktionsschaltfläche klicken.

<div ng-controller="MyCtrl">
    <map></map>
    <button ng-click="updateMap()">call updateMap()</button>
</div>
app.directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {
            $scope.updateMap = function() {
                alert('inside updateMap()');
            }
        }
    }
});

fiddle


Laut @ FlorianFs Kommentar sind die Dinge komplizierter, wenn die Direktive einen isolierten Geltungsbereich verwendet. Hier ist eine Möglichkeit, dies zu erreichen: Fügen Sie der Anweisung mapein Attribut set-fn hinzu, mit dem die Anweisungsfunktion beim Controller registriert wird:

<map set-fn="setDirectiveFn(theDirFn)"></map>
<button ng-click="directiveFn()">call directive function</button>
scope: { setFn: '&' },
link: function(scope, element, attrs) {
    scope.updateMap = function() {
       alert('inside updateMap()');
    }
    scope.setFn({theDirFn: scope.updateMap});
}
function MyCtrl($scope) {
    $scope.setDirectiveFn = function(directiveFn) {
        $scope.directiveFn = directiveFn;
    };
}

fiddle

71
Mark Rajcok

Obwohl es verlockend sein könnte, ein Objekt über den isolierten Geltungsbereich einer Direktive zu informieren, um die Kommunikation mit der Direktive zu vereinfachen, kann dies zu verwirrendem "Spaghetti" -Code führen, insbesondere wenn diese Kommunikation über mehrere Ebenen (Controller, Direktive, Direktive, verschachtelte Direktive usw.) 

Wir gingen ursprünglich diesen Weg ein, aber nachdem weitere Untersuchungen ergeben hatten, dass dies sinnvoller war und zu wartungsfähigerem und lesbarerem Code führte, um Ereignisse und Eigenschaften offenzulegen, die eine Direktive für die Kommunikation über einen Dienst verwenden wird, als $ watch für die Eigenschaften dieses Dienstes in die Richtlinie oder andere Kontrollen, die auf diese Änderungen für die Kommunikation reagieren müssten.

Diese Abstraktion funktioniert sehr gut mit dem Abhängigkeitsinjektions-Framework von AngularJS, da Sie den Service in alle Elemente einfügen können, die auf diese Ereignisse reagieren müssen. Wenn Sie sich die Angular.js-Datei ansehen, werden Sie feststellen, dass die Direktiven dort auch Services und $ watch auf diese Weise verwenden. Sie legen keine Ereignisse über den isolierten Bereich offen.

Für den Fall, dass Sie zwischen voneinander abhängigen Richtlinien kommunizieren müssen, würde ich empfehlen, einen Controller zwischen diesen Richtlinien als Kommunikationsmittel zu teilen.

AngularJS Wiki für Best Practices erwähnt dies auch:

Verwenden Sie nur. $ Broadcast (),. $ Emit () und. $ On () für atomare Ereignisse Ereignisse, die in der gesamten App global relevant sind (z. B. ein Benutzer, der sich authentifiziert oder das Schließen der App). Wenn Sie spezifische Ereignisse für Module, Dienste oder Widgets wünschen, sollten Sie Dienste, Richtliniencontroller oder Drittanbieter-Libs in Betracht ziehen

  • $ scope. $ watch () sollte die Notwendigkeit für Ereignisse ersetzen
  • Das direkte Einspritzen von Diensten und das Aufrufen von Methoden ist auch für die direkte Kommunikation nützlich
  • Direktiven können über Direktive-Controller direkt miteinander kommunizieren
34
Always Learning

Aufbauend auf Olivers Antwort - Sie müssen möglicherweise nicht immer auf die inneren Methoden einer Direktive zugreifen, und in diesen Fällen möchten Sie wahrscheinlich kein leeres Objekt erstellen und der Direktive einen control-Attribut hinzufügen, nur um einen Fehler zu vermeiden (cannot set property 'takeTablet' of undefined). 

Möglicherweise möchten Sie die Methode auch an anderen Stellen in der Direktive verwenden.

Ich würde eine Überprüfung hinzufügen, um sicherzustellen, dass scope.control vorhanden ist, und Methoden auf ähnliche Weise wie das aufdeckende Modulmuster festlegen

app.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{control}}</div>',
    scope: {
      control: '='
    },
    link : function (scope, element, attrs) {
      var takenTablets = 0;
      var takeTablet = function() {
        takenTablets += 1;  
      }

      if (scope.control) {
        scope.control = {
          takeTablet: takeTablet
        };
      }
    }
  };
});
15
CheapSteaks

Um ehrlich zu sein, war ich mit keiner der Antworten in diesem Thread wirklich überzeugt. Hier sind meine Lösungen:

Richtlinien-Handler (Manager) -Ansatz

Diese Methode ist unabhängig davon, ob der $scope der Direktive ein gemeinsam genutzter oder ein isolierter ist

Eine factory zum Registrieren der Direktive

angular.module('myModule').factory('MyDirectiveHandler', function() {
    var instance_map = {};
    var service = {
        registerDirective: registerDirective,
        getDirective: getDirective,
        deregisterDirective: deregisterDirective
    };

    return service;

    function registerDirective(name, ctrl) {
        instance_map[name] = ctrl;
    }

    function getDirective(name) {
        return instance_map[name];
    }

    function deregisterDirective(name) {
        instance_map[name] = null;
    }
});

Den Direktive-Code stelle ich normalerweise die gesamte Logik, die sich nicht mit DOM befasst, in den Direktive Controller. Und die Controller-Instanz in unserem Handler registrieren

angular.module('myModule').directive('myDirective', function(MyDirectiveHandler) {
    var directive = {
        link: link,
        controller: controller
    };

    return directive;

    function link() {
        //link fn code
    }

    function controller($scope, $attrs) {
        var name = $attrs.name;

        this.updateMap = function() {
            //some code
        };

        MyDirectiveHandler.registerDirective(name, this);

        $scope.$on('destroy', function() {
            MyDirectiveHandler.deregisterDirective(name);
        });
    }
})

vorlagencode

<div my-directive name="foo"></div>

Greifen Sie mit factory auf die Controller-Instanz zu und führen Sie die öffentlich bereitgestellten Methoden aus

angular.module('myModule').controller('MyController', function(MyDirectiveHandler, $scope) {
    $scope.someFn = function() {
        MyDirectiveHandler.get('foo').updateMap();
    };
});

Angulars Ansatz

Aus dem eckigen Buch ein Blatt darüber, wie sie damit umgehen

<form name="my_form"></form>

verwenden von $ parse und Registrieren des Controllers im $parent-Bereich. Diese Technik funktioniert nicht mit isolierten $scope-Direktiven. 

angular.module('myModule').directive('myDirective', function($parse) {
    var directive = {
        link: link,
        controller: controller,
        scope: true
    };

    return directive;

    function link() {
        //link fn code
    }

    function controller($scope, $attrs) {
        $parse($attrs.name).assign($scope.$parent, this);

        this.updateMap = function() {
            //some code
        };
    }
})

Zugriff auf den Controller mit $scope.foo

angular.module('myModule').controller('MyController', function($scope) {
    $scope.someFn = function() {
        $scope.foo.updateMap();
    };
});
12
Mudassir Ali

Etwas spät, aber dies ist eine Lösung mit dem isolierten Bereich und "Ereignissen", um eine Funktion in der Direktive aufzurufen. Diese Lösung wird von this SO post by satchmorun inspiriert und fügt ein Modul und eine API hinzu.

//Create module
var MapModule = angular.module('MapModule', []);

//Load dependency dynamically
angular.module('app').requires.Push('MapModule');

Erstellen Sie eine API, um mit der Direktive zu kommunizieren. Das addUpdateEvent fügt ein Ereignis zum Ereignisarray hinzu, und updateMap ruft jede Ereignisfunktion auf.

MapModule.factory('MapApi', function () {
    return {
        events: [],

        addUpdateEvent: function (func) {
            this.events.Push(func);
        },

        updateMap: function () {
            this.events.forEach(function (func) {
                func.call();
            });
        }
    }
});

(Möglicherweise müssen Sie Funktionen hinzufügen, um das Ereignis zu entfernen.)

Legen Sie in der Direktive einen Verweis auf die MapAPI fest und fügen Sie $ scope.updateMap als Ereignis hinzu, wenn MapApi.updateMap aufgerufen wird.

app.directive('map', function () {
    return {
        restrict: 'E', 
        scope: {}, 
        templateUrl: '....',
        controller: function ($scope, $http, $attrs, MapApi) {

            $scope.api = MapApi;

            $scope.updateMap = function () {
                //Update the map 
            };

            //Add event
            $scope.api.addUpdateEvent($scope.updateMap);

        }
    }
});

Fügen Sie im "main" -Controller einen Verweis auf MapApi hinzu und rufen Sie einfach MapApi.updateMap () auf, um die Karte zu aktualisieren.

app.controller('mainController', function ($scope, MapApi) {

    $scope.updateMapButtonClick = function() {
        MapApi.updateMap();    
    };
}
10
AxdorphCoder

Sie können ein DOM-Attribut angeben, das verwendet werden kann, damit die Anweisung eine Funktion im übergeordneten Bereich definieren kann. Der übergeordnete Bereich kann diese Methode dann wie jede andere aufrufen. Hier ist ein Plunker. Und unten ist der entsprechende Code.

clearfn ist ein Attribut für das Direktive-Element, an das der übergeordnete Bereich eine Bereichseigenschaft übergeben kann, die die Direktive auf eine Funktion setzen kann, die das gewünschte Verhalten erfüllt.

<!DOCTYPE html>
<html ng-app="myapp">
  <head>
    <script data-require="[email protected]*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <style>
      my-box{
        display:block;
        border:solid 1px #aaa;
        min-width:50px;
        min-height:50px;
        padding:.5em;
        margin:1em;
        outline:0px;
        box-shadow:inset 0px 0px .4em #aaa;
      }
    </style>
  </head>
  <body ng-controller="mycontroller">
    <h1>Call method on directive</h1>
    <button ng-click="clear()">Clear</button>
    <my-box clearfn="clear" contentEditable=true></my-box>
    <script>
      var app = angular.module('myapp', []);
      app.controller('mycontroller', function($scope){
      });
      app.directive('myBox', function(){
        return {
          restrict: 'E',
          scope: {
            clearFn: '=clearfn'
          },
          template: '',
          link: function(scope, element, attrs){
            element.html('Hello World!');
            scope.clearFn = function(){
              element.html('');
            };
          }
        }
      });
    </script>
  </body>
</html>
5
Trevor

Benutzen Sie einfach den Bereich

angular.module('myApp', [])
.controller('MyCtrl',['$scope',function($scope) {

}])
.directive('mydirective',function(){
 function link(scope, el, attr){
   //use scope.$parent to associate the function called to directive function
   scope.$parent.myfunction = function directivefunction(parameter){
     //do something
}
}
return {
        link: link,
        restrict: 'E'   
      };
});

in HTML

<div ng-controller="MyCtrl">
    <mydirective></mydirective>
    <button ng-click="myfunction(parameter)">call()</button>
</div>
2
ramon prata

Sie können den Methodennamen an Direktive angeben, um festzulegen, welche Sie vom Controller aus aufrufen möchten, jedoch ohne Isolationsbereich.

angular.module("app", [])
  .directive("palyer", [
    function() {
      return {
        restrict: "A",
        template:'<div class="player"><span ng-bind="text"></span></div>',
        link: function($scope, element, attr) {
          if (attr.toPlay) {
            $scope[attr.toPlay] = function(name) {
              $scope.text = name + " playing...";
            }
          }
        }
      };
    }
  ])
  .controller("playerController", ["$scope",
    function($scope) {
      $scope.clickPlay = function() {
        $scope.play('AR Song');
      };
    }
  ]);
.player{
  border:1px solid;
  padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
  <div ng-controller="playerController">
    <p>Click play button to play
      <p>
        <p palyer="" to-play="play"></p>
        <button ng-click="clickPlay()">Play</button>

  </div>
</div>

2
Naveen raj

GETESTET Hoffe das hilft jemandem.

Mein einfacher Ansatz (Think-Tags als ursprünglichen Code)

<html>
<div ng-click="myfuncion"> 
<my-dir callfunction="myfunction">
</html>

<directive "my-dir">
callfunction:"=callfunction"
link : function(scope,element,attr) {
scope.callfunction = function() {
 /// your code
}
}
</directive>
1
Santosh Kumar

Vielleicht ist dies nicht die beste Wahl, aber Sie können mit angular.element("#element").isolateScope() oder $("#element").isolateScope() auf den Geltungsbereich und/oder den Controller Ihrer Direktive zugreifen.

0
Alex198710

Die folgende Lösung ist nützlich, wenn Sie Controller (sowohl übergeordnet als auch direkt (isoliert)) im Format "Controller As" haben

jemand könnte dies nützlich finden,

direktive:

var directive = {
        link: link,
        restrict: 'E',
        replace: true,
        scope: {
            clearFilters: '='
        },
        templateUrl: "/temp.html",
        bindToController: true, 
        controller: ProjectCustomAttributesController,
        controllerAs: 'vmd'
    };
    return directive;

    function link(scope, element, attrs) {
        scope.vmd.clearFilters = scope.vmd.SetFitlersToDefaultValue;
    }
}

direktion Controller: 

function DirectiveController($location, dbConnection, uiUtility) {
  vmd.SetFitlersToDefaultValue = SetFitlersToDefaultValue;

function SetFitlersToDefaultValue() {
           //your logic
        }
}

hTML Quelltext : 

      <Test-directive clear-filters="vm.ClearFilters"></Test-directive>
    <a class="pull-right" style="cursor: pointer" ng-click="vm.ClearFilters()"><u>Clear</u></a> 
//this button is from parent controller which will call directive controller function
0
Raunak Mali

So erhalten Sie den Controller einer Direktive in einen Seitencontroller:

  1. schreiben Sie eine benutzerdefinierte Direktive, um den Verweis auf den Direktive Controller vom DOM-Element abzurufen: 

    angular.module('myApp')
        .directive('controller', controller);
    
    controller.$inject = ['$parse'];
    
    function controller($parse) {
        var directive = {
            restrict: 'A',
            link: linkFunction
        };
        return directive;
    
        function linkFunction(scope, el, attrs) {
            var directiveName = attrs.$normalize(el.prop("tagName").toLowerCase());
            var directiveController = el.controller(directiveName);
    
            var model = $parse(attrs.controller);
            model.assign(scope, directiveController);
        }
    }
    
  2. verwenden Sie es in der HTML-Seite des Seitencontrollers:

    <my-directive controller="vm.myDirectiveController"></my-directive>
    
  3. Verwenden Sie den Direktionscontroller im Seitencontroller:

    vm.myDirectiveController.callSomeMethod();
    

Hinweis: Die angegebene Lösung funktioniert nur für Controller von Element-Direktiven (Tag-Name wird verwendet, um den Namen der gewünschten Direktive zu erhalten).

0
Robert J