Вызов методов директивы от родительского контроллера в AngularJS
Я использую AngularJS с шаблоном контроллеров псевдонимов. Я не могу получить доступ (или не знаю, как) директивные методы от родительского контроллера.
У меня есть функция внутри моего контроллера, которая должна вызывать метод директивы, но этот директивный метод недоступен внутри значения контроллера this
.
Это то, что у меня есть. Что я делаю неправильно?
JS
angular.module('myApp', []).
controller('MyCtrl', function(){
this.text = 'Controller text';
this.dirText = 'Directive text';
this.click = function(){
this.changeText();
}
})
.directive('myDir', function(){
return {
restrict: 'E',
scope: {
text: '='
},
link: function(scope, element, attrs){
scope.changeText = function(){
scope.text = 'New directive text';
};
},
template: '<h2>{{text}}</h2>'
};
});
HTML
<div ng-app="myApp">
<div ng-controller="MyCtrl as ctrl">
<h1>{{ctrl.text}}</h1>
<my-dir text="ctrl.dirText"></my-dir>
<button ng-click="ctrl.click()">Change Directive Text</button>
</div>
</div>
Здесь код с кодом.
Ответы
Ответ 1
Если вы строго хотите использовать изолированный scope
внутри директивы, тогда директивный метод можно вызывать только с помощью событий angular, таких как $broadcast
и $emit
В вашем случае вам нужно использовать $broadcast
для отправки события на весь $rootScope
Код будет выглядеть следующим образом.
Ручка рабочего кода
HTML
<div ng-app="myApp">
<div ng-controller="MyCtrl as ctrl">
<h1>{{ctrl.text}}</h1>
<my-dir text="ctrl.dirText"></my-dir>
<button ng-click="ctrl.click()">Change Directive Text</button>
</div>
</div>
CODE
angular.module('myApp', []).
controller('MyCtrl', function($rootScope){
var that = this;
this.text = 'Controller text';
this.dirText = 'Directive text';
this.click = function(){
$rootScope.$broadcast('changeText',{});
}
}).
directive('myDir', function(){
return {
restrict: 'E',
scope: {
text: '='
},
link: function(scope, element, attrs){
scope.changeText = function(){
scope.text = 'New directive text';
};
scope.$on('changeText',function(event, data){
scope.changeText()
});
},
template: '<h2>{{text}}</h2>'
};
});
Вместо вызова метода дочерней области вам необходимо передать событие, и его нужно будет прослушать с помощью области действия директивы, и после прослушивания этого события будет срабатывать метод changeText
.
Примечание
Использование сервиса / factory было бы лучшим подходом.
Это, надеюсь, поможет вам. Спасибо.
Ответ 2
Вы можете получить вызовы директивных методов, не полагаясь на $broadcast или удаление изоляции. Подобные подходы, которые были размещены здесь до сих пор, будут разбиты, если на странице есть 2+ экземпляра директивы (все они будут отражать те же изменения).
Этот код демонстрирует более надежный способ сделать это.
angular.module('myApp', [])
.controller('myChat', function($scope) {
function room () {return { accessor:{} }; }
$scope.rooms = { 'RoomA': new room, 'RoomB': new room, 'RoomC': new room };
$scope.addMessageTo = function(roomId, msg) {
if ($scope.rooms[roomId].accessor.setMessage)
$scope.rooms[roomId].accessor.setMessage(msg);
};
$scope.addMessages = function () {
$scope.addMessageTo("RoomA", "A message");
$scope.addMessageTo("RoomB", "Two messages");
$scope.addMessageTo("RoomC", "More messages");
}
}).directive('myChatRoom', function() {
return {
template: '<div>{{room}} message = {{message}}<div />',
scope: { accessor: "=", room: "@" },
link: function (scope) {
if (scope.accessor) {
scope.accessor.setMessage = function(msg) {
scope.message = msg;
};
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myChat">
<div ng-repeat="(roomId, room) in rooms">
<div my-chat-room room="{{roomId}}" accessor="room.accessor"></div>
</div>
<button ng-click="addMessages()">Add messages to rooms</button>
</div>
</div>
Ответ 3
У меня есть другое решение, которое позволяет использовать область выделения и не полагаться на трансляцию. В javascript методы могут использоваться как переменные, co вы можете просто передать метод, который вы хотите в директиве.
поэтому в html:
<my-dir text="ctrl.dirText" change-text="ctrl.changeText"></my-dir>
и в директиве
scope: {
text: '=',
changeText: '='
}
Здесь немного modyfied codepen, где вы можете видеть, что я имею в виду.
Ответ 4
Вы выделяете область при написании:
scope: {
text: '='
},
Здесь немного измененная версия вашего кода, на этот раз, позволяет вам вызвать метод директивы. В основном я просто избавился от 'scope'
в директиве и изменил его на использование $scope в контроллере, а не на этом, и шаблон Alias.
ПРЕДУПРЕЖДЕНИЕ. Это может не отражать правильное поведение в отношении какие переменные меняются, но отвечает на ваш вопрос, показывая, как вы можете получить доступ к директивному методу от контроллера. Обычно это не хорошая идея дизайна.
http://codepen.io/anon/pen/azwJBm
angular.module('myApp', []).
controller('MyCtrl', function($scope){
var that = this;
$scope.text = 'Controller text';
$scope.dirText = 'Directive text';
$scope.click = function(){
$scope.changeText();
}
}).
directive('myDir', function(){
return {
restrict: 'E',
/* scope: {
text: '='
},*/
link: function(scope, element, attrs){
scope.changeText = function(){
scope.text = 'New directive text';
};
},
template: '<h2>{{text}}</h2>'
};
});
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<h1>{{text}}</h1>
<my-dir text="dirText"></my-dir>
<button ng-click="click()">Change Directive Text</button>
</div>
</div>
Ответ 5
Попробовав как объекты $broadcast
, так и control
, я бы рекомендовал попробовать привязать значения или массивы. Объект control
- это простой способ достижения желаемого результата, но в моем тестировании очень неоткрываемо и подвержено ошибкам.
Этот Codepen создает пример BernardV, но использует массив сообщений как очень заметное связывание с контролем. Если вы хотите, вы можете легко $watch
массива сообщений внутри директивы. Основная идея заключается в использовании директивы:
scope: { messages: "=", room: "@" },
Из контроллера (имеющего массив "комнат" ) вы сделаете следующее:
$scope.addMessages = function () {
angular.forEach($scope.rooms, function(room, index) {
room.messages.push("A new message! # " + (index+1);
})
}
Независимые директивы, независимые сообщения и высокодоступные. Разумеется, в директиве можно показать только последнее сообщение или просто привязать строку вместо массива. Это решение работало намного лучше для нас, по крайней мере.