web-dev-qa-db-fra.com

AngularJS: événement de diffusion de directive

J'ai vu des gens faire cela n'importe où dans leur code:

$rootScope.$broadcast('someEvent', someParameter); 

puis dans un contrôleur:

$rootScope.$on('someEvent', function(event, e){ /* implementation here */ });

Maintenant, je voudrais diffuser un événement d'une directive. Est-ce une bonne pratique de le diffuser au niveau rootScope? Je voudrais gérer cet événement dans un contrôleur. Puis-je utiliser $ scope ou dois-je toujours écouter sur $ rootScope? 

53
Sam

Dans mon cas, je veux simplement diffuser un événement d'une directive au contrôleur de la vue, dans lequel j'utilise la directive. Cela a-t-il encore un sens d'utiliser la diffusion alors?

J'aurais la directive appeler une méthode sur le contrôleur, qui est spécifiée dans le code HTML où la directive est utilisée:

Pour une directive utilisant une portée isolée:

<div my-dir ctrl-fn="someCtrlFn(arg1)"></div>

app.directive('myDir', function() {
  return {
    scope: { ctrlFn: '&' },
    link: function(scope, element, attrs) {
       ...
       scope.ctrlFn({arg1: someValue});
    }

Pour une directive qui n'utilise pas d'étendue isolée:

<div my-dir ctrl-fn="someCtrlFn(arg1)"></div>

app.directive('myDir', function($parse) {
  return {
    scope: true,  // or no new scope -- i.e., remove this line
    link: function(scope, element, attrs) {
       var invoker = $parse(attrs.ctrlFn);
       ...
       invoker(scope, {arg1: someValue} );
    }
78
Mark Rajcok

Il est généralement judicieux de ne pas utiliser $ rootScope car il est global et vous ne devriez pas le polluer à moins de savoir vraiment ce que vous faites. Je vous recommande de lire cet article sur la communication entre services, directives et contrôleurs .

13
joakimbl

Voici un exemple TypeScript montrant comment rappeler une méthode sur le contrôleur à partir d'une directive incorporée. La chose la plus importante à noter est que le nom de paramètre de la directive pour votre rappel utilise un & lorsqu'il est défini, et lorsque vous appelez ce rappel, vous ne devez pas utiliser de paramètres positionnels, mais plutôt utiliser un objet avec des propriétés ayant les noms des paramètres dans la cible.

Enregistrez la directive lorsque vous créez votre module d'application:

module MyApp {
    var app: angular.IModule = angular.module("MyApp");
    MyApp.Directives.FileUploader.register(app);
}

Le code d'enregistrement est le suivant:

module MyApp.Directives.FileUploader {
  class FileUploaderDirective implements angular.IDirective {
      public restrict: string = "E";
      public templateUrl: string = "/app/Directives/FileUploader/FileUploaderDirective.html";

      //IMPORTANT - Use & to identify this as a method reference
      public scope: any = {
        onFileItemClicked: "&"
      };
      public controller: string = "MyApp.Directives.FileUploader.Controller";
      public controllerAs: string = "controller";
      public bindToController: boolean = true;
      public transclude: boolean = true;
      public replace: boolean = true;
  }

  export function register(app: angular.IModule) {
      app.controller("MyApp.Directives.FileUploader.Controller", Controller);
      app.directive("fileUploader", () => new FileUploaderDirective());
  }
}

Le contrôleur de la directive ressemblerait à ceci

module MyApp.Directives.FileUploader {
    export class Controller {
        public files: string[] = ["One", "Two", "Three"];
        //The callback specified in the view that created this directive instance
        public onFileItemClicked: (fileItem) => void;

        // This is the controller method called from its HTML's ng-click
        public fileItemClicked(fileItem) {
            //IMPORTANT: Don't use comma separated parameters,
            //instead use an object with property names to act as named parameters
            this.onFileItemClicked({
                fileItem: fileItem
            });
        }
    }
}

Le code HTML de la directive ressemblerait à quelque chose comme ça

<ul>
  <li ng-repeat="item in controller.files" ng-click="controller.fileItemClicked (item)">
    {{ item }}
  </li>
</ul>

La vue principale aura une instance de votre directive comme suit

<body ng-app="MyApp" ng-controller="MainController as controller">
  <file-uploader on-file-item-clicked="controller.fileItemClicked(fileItem)"/>
</body>

Maintenant, tout ce dont vous avez besoin sur votre MainController est une méthode 

public fileItemClicked(fileItem) {
  alert("Clicked " + fileItem);
}
0
Peter Morris