Подтвердить что ты не робот

Как вывести поведение из директивы с изолированной областью?

Как я могу выставить метод из директивы? Я знаю, что я должен использовать атрибуты для данных, но я действительно хочу разоблачить поведение, а не данные. То, что может вызвать родительский контроллер.

Скажем, мой DOM выглядит так:

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

Если scope закомментирован (т.е. директива не имеет изолированной области), она работает нормально. Когда я нажимаю кнопку, вызывается myfn и записывается в консоль.

Как только я раскомментирую scope, он не работает. myfn определяется по охвату пользователя и недоступен для родителя.

В моем случае я считаю, что загрязнение родительской области - плохая идея, и я действительно хотел бы избежать ее.

Итак, как я могу выставить функцию из директивы в родительский контроллер? Или: Как я могу вызвать метод для директивы родительского контроллера?

4b9b3361

Ответ 1

Вы можете сделать это с изолированной областью, настроив переменную в области, которая привязана к контроллеру с двух сторон (с использованием "=" ). В вашей директиве вы можете назначить функцию этой переменной, а angular будет использовать привязку, чтобы найти соответствующую переменную в вашем контроллере. Эта переменная укажет на функцию, которую может вызывать ваш контроллер.

http://jsfiddle.net/GWCCr/

html: обратите внимание на новый атрибут:

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

Ответ 2

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

Одна из причин, по которой я могу думать, - это вызвать некоторое поведение директивы, которое также может быть вызвано пользователем приложения изнутри этой директивы.

Если это так, то очевидная и зависящая от него задача состояла бы в том, чтобы транслировать событие в области, содержащей директиву, которая должна реагировать на нее. Тогда директива будет слушать это событие и инициировать его функцию сама по себе.

Это дает дополнительные преимущества:

  • вы можете отправлять параметры в объект данных события, если хотите
  • вы можете вызвать реакцию в нескольких директивах (например, если они образуют коллекцию)
  • вы можете связываться с директивой, которая находится глубоко в иерархии областей (например, если директива находится внутри другой директивы, которая находится внутри других директив и т.д.), не передавая функцию обратного вызова через каждую вложенную директиву
  • он не нарушает выделение директивы

Пример

Попробуем придумать очень простой пример: предположим, у нас есть виджет, который отображает случайную вдохновляющую цитату, загруженную откуда-то. Он также имеет кнопку, чтобы изменить цитату на другую.

Здесь шаблон директивы:

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

И вот код директивы:

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

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

Предположим, что мы также хотим обновить цитату из контроллера. Это может быть так же просто, как вызвать это в коде контроллера:

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

Чтобы добавить обработчик событий, мы должны добавить этот код к функции link директивы:

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

Таким образом, мы создали односторонний канал связи от контроллера к директиве, которая не нарушает изоляцию директивы, а также работает, если директива глубоко вписана в иерархию областей кода, которая транслирует событие.

Ответ 3

Как я могу выставить функцию из директивы родительскому контроллеру?
Или: Как я могу вызвать метод для директивы родительского контроллера?

Ну, я не думаю, что вы должны пытаться это сделать (например, поведение контроллера связи с директивой), но если вы должны... здесь один из способов сделать это: передайте функцию контроллера в свою директиву, который директива может вызвать для уведомления диспетчера директивной функции:

<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

Ответ 4

Чтобы внести свой вклад, @georgeawg дал мне классное решение, используя Service, чтобы выполнить эту работу. Таким образом, вы можете обрабатывать несколько директив на одной странице.

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

Ответ 5

В выпуске AngularJS V1.7.1 * введена новая директива ng-ref.

Атрибут ng-ref указывает AngularJS опубликовать контроллер компонента в текущей области. Это полезно для того, чтобы такой компонент, как аудиоплеер, предоставлял свой API-интерфейс родственным компонентам. Его элементы управления воспроизведением и остановкой легко доступны.

Для получения дополнительной информации см.