AngularJS: ng-controller в директиве не работает с включенными элементами в директиве

Вот мой сценарий:

angular.module('MyApp',[])
.directive('mySalutation',function(){
    return {
        restrict:'E',
        scope:true,
        replace:true,
        transclude:true,
        template:'<div>Hello<div ng-transclude></div></div>',
        link:function($scope,$element,$attrs){
        }
    };
})
.controller('SalutationController',['$scope',function($scope){
    $scope.target = "StackOverflow";
}])

и html:

<body ng-app="MyApp">
    <my-salutation ng-controller="SalutationController">
        <strong>{{target}}</strong>        
    </my-salutation>
</body>

Проблема в том, что когда SalutationController применяется к директиве my-salutation, $scope.target не отображается для включенного элемента. Но если я помещаю ng-controller в элемент <body> или <strong>, это работает. Как говорится в docs, ng-controller создает новую область.

  • Кто может объяснить, как в данном случае мешают друг другу сфера действия и сфера действия директивы?

  • Как я могу поместить контроллер в директиву? Любые подсказки будут оценены.


person Engineer    schedule 22.03.2014    source источник
comment
Это необходимо? Директивы могут принимать параметр controller в объекте в дополнение к параметру link.   -  person jedd.ahyoung    schedule 27.11.2014
comment
@jedd.ahyoung Да, вы можете добавить параметр контроллера в директиву, но в этом случае у вас будет один и тот же контроллер для всех экземпляров директивы. Что, если мне не нужен контроллер для моей директивы или мне нужен другой контроллер?!   -  person Engineer    schedule 28.11.2014


Ответы (2)


1) Проблема в том, что область действия ng-transclude является областью sibling вашей директивы. Когда вы помещаете ng-controller в родительский элемент, область, созданная ng-controller, является родительской областью как вашей директивы, так и ng-transclude. Из-за наследования области включенный элемент может правильно связать {{target}}.

2) Вы можете сделать это, используя пользовательское включение, чтобы привязать область самостоятельно.

.directive('mySalutation',function(){
    return {
        restrict:'E',
        scope:true,
        replace:true,
        transclude:true,
        template:'<div>Hello<div class="transclude"></div></div>',
        compile: function (element, attr, linker) {
            return function (scope, element, attr) {
                linker(scope, function(clone){
                       element.find(".transclude").append(clone); // add to DOM
                });

            };
        }
    };
})

ДЕМО

Или используя функцию transclude в функции ссылки:

.directive('mySalutation',function(){
    return {
        restrict:'E',
        scope:true,
        replace:true,
        transclude:true,
        template:'<div>Hello<div class="transclude"></div></div>',
        link: function (scope, element, attr,controller, linker) {
           linker(scope, function(clone){
                  element.find(".transclude").append(clone); // add to DOM
           });
        }
    };
})

ДЕМО

person Khanh TO    schedule 22.03.2014
comment
Состояния документа: Примечание. Функция преобразования, которая передается функции компиляции. устарел, так как, например. не знает о правильной внешней области видимости. Вместо этого используйте функцию преобразования, которая передается функции ссылки. Это новое в Angular 1.2, согласно этот пост. - person kamituel; 22.03.2014
comment
@kamituel: Спасибо, я не знал, что это изменилось. Я обновил ответ (идея та же) - person Khanh TO; 22.03.2014
comment
Если я правильно понял ваш первый пункт, это означает, что если я не создам новую область действия для директивы, то есть scope будет установлен в false, то код должен работать. Почему это не работает в таком случае? - person Engineer; 22.03.2014
comment
@Engineer: Когда я отлаживал. Я заметил, что нет никакой разницы с true, false. В обоих случаях областью действия директивы является область действия контроллера (тот же $id). Я думаю, что angular не создает область, если она уже есть, даже если scope:true - person Khanh TO; 22.03.2014
comment
Обратите внимание, что find() поддерживает только имена тегов, поэтому вам может потребоваться выполнить angular.element(element[0].querySelector('.classname')).append(clone). Кроме того, если вы используете изолированную область, вам может потребоваться выполнить linker(scope.$parent, ...) - person devdigital; 01.01.2015
comment
@devdigital: это не будет проблемой, если мы включим jQuery на страницу. - person Khanh TO; 02.01.2015

Чтобы иметь одинаковую область действия для директивы и контроллера, вы можете вызвать transcludeFn вручную:

angular.module('MyApp',[])
.directive('mySalutation',function(){
    return {
        restrict:'E',
        scope:true,
        replace:true,
        transclude:true,
        template:'<div>Hello<div class="trans"></div></div>',
        link:function(scope, tElement, iAttrs, controller, transcludeFn){
                console.log(scope.$id);
                transcludeFn(scope, function cloneConnectFn(cElement) {
                    tElement.after(cElement);
                }); 
        }
    };
})
.controller('SalutationController',['$scope',function($scope){
    console.log($scope.$id);
    $scope.target = "StackOverflow";
}]);

сбросить

Вы можете видеть, что «003» каждый раз выходит из системы, и ваш код работает, как и ожидалось, с этой небольшой корректировкой.

person Oleg Belousov    schedule 22.03.2014