Контроллеры директив AngularJS, требующие родительских контроллеров директив?

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

.directive('screen', function() {
    return {
        scope: true,
        controller: function() {
            this.doSomethingScreeny = function() {
                alert("screeny!");
            }
        }
    }
})

.directive('component', function() {
    return {
        scope: true,
        controller: function() {
            this.componentFunction = function() {
                WHAT.doSomethingScreeny();
            }
        }
    }
})

.directive('widget', function() {
    return {
        scope: true,
        require: "^component",
        link: function(scope, element, attrs, componentCtrl) {
            scope.widgetIt = function() {
                componentCtrl.componentFunction();
            };
        }
    }
})

<div screen>
    <div component>
        <div widget>
            <button ng-click="widgetIt()">Woo Hoo</button>
        </div>
    </div>
</div>

Я могу запросить родительские компоненты в ссылке виджета fn, используя require: "^component", но как мне дополнительно предоставить контроллеру компонентов доступ к содержащему его экрану?

Что мне нужно, так это ЧТО в компоненте, поэтому, когда вы нажимаете кнопку виджета, он предупреждает «экранный!».

Спасибо.


person nicholas    schedule 25.03.2013    source источник


Ответы (4)


Вот два способа решить вашу проблему:

  1. Поскольку вы используете scope: true, прототипно все области наследуются. Поэтому, если вы определите свои методы для $scope вместо this в контроллере screen, то и component, и widget будут иметь доступ к функции doSomethingScreeny.
    Скрипка.
  2. Определите функцию связи на component и require: '^screen'. В функции ссылки сохраните screenCtrl в свойстве области действия, после чего вы сможете получить к нему доступ в контроллере директивы (введите $scope).
    Скрипка.
person Mark Rajcok    schedule 25.03.2013
comment
Спасибо. В итоге я остановился на вашем варианте 1. Но в любом случае кажется, что вам нужно бросить все на прицел, поэтому я не уверен, что полностью понимаю смысл контроллера. Вернее, для чего он будет использоваться. Конечно, я провел с AngularJS всего пару часов. Какие-нибудь полезные руководства, на которые вы могли бы мне указать, которые объясняют отношения между службами, контроллерами и директивами? Спасибо. - person nicholas; 27.03.2013
comment
@nicholas, все, что вы хотите показать в представлении, должно быть в связанной $scope. Таким образом, контроллер является своего рода связующим звеном между вашими моделями (которые часто находятся в сервисах) и вашим представлением. Контроллеры также добавляют поведение к представлению через функции. См. также stackoverflow.com/a/13482008/215945 и stackoverflow.com/questions/ 14994391/ - person Mark Rajcok; 27.03.2013
comment
@MarkRajcok, я обновил скрипту, которая демонстрирует ваш второй вариант, чтобы он более точно соответствовал fiddle для вашего первого варианта. Я сделал это, включив контроллеры директив экрана и компонентов в функцию link виджета. Это изменение демонстрирует, как директива может require использовать более одного контроллера, используя массив строк. - person chuck w; 29.09.2014
comment
Контроллеры позволяют вашим директивам взаимодействовать (требуется только ссылка на контроллер), а контроллеры ВСЕГДА легче тестировать, чем директивы, потому что они не требуют, чтобы вы имитировали DOM или шаблон при тестировании. - person Josh G; 18.10.2014

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

.directive('screen', function ($controller) {
    return {
       require: '^parent',
       scope: {},
       link: function (scope, element, attr, controller) {
           $controller('MyCtrl', {
                $scope: scope,
                $element: element,
                $attr, attr, 
                controller: controller
           });
       }
    }
})

.controller('MyCtrl, function ($scope, $element, $attr, controller) {});

Этот метод лучше тестируется и не загрязняет вашу область нежелательными контроллерами.

person malte    schedule 10.03.2015

return {scope: true} или return {scope: false} не влияет на переменную $scope в контроллере: function($scope) {} в каждой директиве, но тег директивы должен быть помещен в тег ng-controller или ng-app .

JSFiddle

JSFiddle

person Lighthouse Nguyen    schedule 02.12.2015

var myApp = angular.module('myApp', [])

.directive('screen', function() {
  return {
    scope: true,
    controller: function() {
      this.doSomethingScreeny = function() {
        alert("screeny!");
      }
    }
  }
})

.directive('component', function() {
  return {
    scope: true,
    controller: function($element) {
      this.componentFunction = function() {
        $element.controller('screen').doSomethingScreeny();
      }
    }
  }
})

.directive('widget', function() {
  return {
    scope: true,
    controller: function($scope, $element) {
      $scope.widgetFunction = function() {
        $element.controller('component').componentFunction();
      }
    }
  }
})

.controller('MyCtrl', function($scope) {
  $scope.name = 'Superhero';
})
<body ng-app="myApp">

  <div ng-controller="MyCtrl">
    <div screen>
      <div component>
        <div widget>
          <button ng-click="widgetFunction()">Woo Hoo</button>
        </div>
      </div>
    </div>
  </div>

</body>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>

Если вы хотите получить доступ к функции, определенной в контроллере директивы screen, из контроллера директивы component (а не функции ссылки), вы можете использовать $element.controller('screen').doSomethingScreeny() (из component > директива).

JSFiddle

Angular документация:

  • controller(name) — извлекает контроллер текущего элемента или его родителя. По умолчанию получает контроллер, связанный с директивой ngController. Если в качестве имени директивы camelCase указано name, то будет получен контроллер для этой директивы (например, 'ngModel').
person Felix    schedule 28.01.2016