it-roy-ru.com

AngularJS: трансляция события из директивы

Я видел людей, делающих это откуда угодно в своем коде:

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

а затем в каком-то контроллере:

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

Теперь я хотел бы передать событие из директивы. Это хорошая практика для трансляции на уровне rootScope? Я хотел бы обработать это событие в контроллере. Могу ли я использовать $ scope или мне все еще нужно прослушивать $ rootScope?

53
Sam

В моем случае я просто хочу передать событие из директивы в контроллер представления, в котором я использую директиву. Есть ли смысл использовать трансляцию?

Я хотел бы, чтобы директива вызывала метод на контроллере, который указан в HTML, где используется директива:

Для директивы, которая использует изолированную область:

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

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

Для директивы, которая не использует изолированную область:

<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

Обычно хорошей идеей является не использовать $ rootScope, поскольку он глобален, и вы не должны загрязнять его, если вы действительно не знаете, что делаете. Я бы порекомендовал вам прочитать эту статью о связи между службами, директивами и контроллерами .

13
joakimbl

Вот пример TypeScript для вызова метода на контроллере из встроенной директивы. Самое важное, на что следует обратить внимание, это то, что имя параметра директивы для вашего обратного вызова использует &, когда оно определено, и при вызове этого обратного вызова вы не должны использовать позиционные параметры, а вместо этого использовать объект со свойствами, имена которых имеют параметры в целевом объекте.

Зарегистрируйте директиву при создании модуля приложения:

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

Регистрационный код выглядит следующим образом:

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

Контроллер директивы будет выглядеть так

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

HTML-код директивы будет выглядеть примерно так

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

Главный вид будет иметь экземпляр вашей директивы, например, так

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

Теперь все, что вам нужно на вашем MainController, это метод 

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