Ng-click не работает в шаблоне директивы
Angular noob здесь. Я создаю директиву для рекурсивного отображения дерева вопросов и подзаголовков. Я использую ссылку в шаблоне, которая вызывает функцию внутри области. По какой-то причине он не вызывает метод editQuestion()
.
Здесь код и скрипка http://jsfiddle.net/madhums/n9KNv/
HTML:
<div ng-controller="FormCtrl">
<questions value="survey.questions"></questions>
</div>
JavaScript:
var app = angular.module('myApp', []);
function FormCtrl ($scope) {
$scope.editQuestion = function (question) {
alert('abc');
};
$scope.survey = {
// ...
}
}
app.directive('questions', function($compile) {
var tpl = '<ol ui-sortable' +
' ng-model="value"' +
' class="list">' +
' <li ng-repeat="question in value | filter:search"' +
' <a href="" class="question">' +
' {{ question.name }}' +
' </a>' +
' <span class="muted">({{ question.type }})</span>' +
' <a href="" class="danger" ng-click="removeQuestion(question)">remove</a>' +
' <a href="" class="blue" ng-click="editQuestion(question)">edit</a>' +
' <choices value="question.choices"></choices>' +
' </li>' +
'</ol>';
return {
restrict: 'E',
terminal: true,
scope: { value: '=' },
template: tpl,
link: function(scope, element, attrs) {
$compile(element.contents())(scope.$new());
}
};
});
app.directive('choices', function($compile) {
var tpl = '<ul class="abc" ng-repeat="choice in value">'+
' <li>' +
' {{ choice.name }}' +
' <span class="muted">' +
' ({{ choice.questions.length }} questions)' +
' </span>' +
'' +
' <a href=""' +
' ng-click="addQuestions(choice.questions)"' +
' tooltip="add sub questions">' +
' +' +
' </a>' +
'' +
' <questions value="choice.questions"></questions>'
' </li>' +
'</ul>';
return {
restrict: 'E',
terminal: true,
scope: { value: '=' },
template: tpl,
link: function(scope, element, attrs) {
$compile(element.contents())(scope.$new());
}
};
});
Любая помощь в понимании этого будет оценена по достоинству.
Ответы
Ответ 1
У вас проблема с областью. Поскольку вы используете изолированную область действия в своей директиве с scope: { value: '=' }
, она больше не имеет доступа к области вашего контроллера, которая имеет editQuestion
.
Вам нужно передать editQuestion
в область действия вашей директивы, чтобы он знал, как ее называть. Это, как правило, довольно легко, но из-за вашей бесконечно рекурсивной директивной структуры, где выбор может включать вопросы, это становится немного сложнее. Здесь работает скрипка:
http://jsfiddle.net/n9KNv/14/
В HTML теперь включена ссылка на editQuestion
:
<div ng-controller="FormCtrl">
<questions value="survey.questions" on-edit="editQuestion(question)"></questions>
</div>
И теперь ваша директива вопросов ожидает в своей области атрибута onEdit
:
app.directive('questions', function($compile) {
var tpl = '<ol ui-sortable' +
' ng-model="value"' +
' class="list">' +
' <li ng-repeat="question in value | filter:search"' +
' <a href="" class="question">' +
' {{ question.name }}' +
' </a>' +
' <span class="muted">({{ question.type }})</span>' +
' <a href="" class="blue" ng-click="onEdit({question: question})">edit</a>' +
' <choices value="question.choices" on-edit="onEdit({question: subQuestion})"></choices>' +
' </li>' +
'</ol>';
return {
restrict: 'E',
terminal: true,
scope: { value: '=', onEdit: '&' },
template: tpl,
link: function(scope, element, attrs) {
$compile(element.contents())(scope.$new());
}
};
});
app.directive('choices', function($compile) {
var tpl = '<ul class="abc" ng-repeat="choice in value">'+
' <li>' +
' {{ choice.name }}' +
' <span class="muted">' +
' ({{ choice.questions.length }} questions)' +
' </span>' +
'' +
' <questions value="choice.questions" on-edit="onEdit({subQuestion: question})"></questions>'
' </li>' +
'</ul>';
return {
restrict: 'E',
terminal: true,
scope: { value: '=', onEdit: '&' },
template: tpl,
link: function(scope, element, attrs) {
$compile(element.contents())(scope.$new());
}
};
});
Обратите внимание, как мы нацеливаем question
в ng-click
. Это то, как вы настраиваете аргументы в функциях обратного вызова. Также обратите внимание, как в on-edit
мы переходим к вашей директиве choices
, мы нацеливаем subQuestion
. Это связано с тем, что question
уже зарезервирован внутри ngRepeat
, поэтому нам нужно провести различие между ними.
Это было, наверное, самое сложное понятие для меня, чтобы учиться в Angular до сих пор. Как только вы поймете, как работает область действия между контроллерами, директивами и другими директивами, мир Angular является вашим.:)
Ответ 2
Это проблема масштаба. Директива ng-click вызывает текущую область редактирования editQuestion и методы removeQuestion, которые не существуют в области директивы, поскольку они определены в модуле, который включает директиву (то есть родительскую область).
Вы хотите установить привязку между директивой и родителем, поэтому, когда директива вызывает функцию ngClick, она запускается в модуле, на котором размещена директива.
Либо вы можете определить методы в самой директиве, либо установить привязку через раздел области действия объект определения директивы
Здесь находится плункер, который иллюстрирует стрельбу по ng-click событиям в разных областях (выходы на консоль)
http://plnkr.co/edit/9XfXCpU6lhUOqD6nbVuQ?p=preview
Ответ 3
Langdon May10 '13 ответ правильный. В демонстрационных целях я упростил код скрипта Langdon и сбил его с 148 строк angular до 23 строк angular.
Я также добавил функциональность, которая позволяет передавать значение параметра как объект MouseEvent через метод вызова функции и извлекать указанное значение в функции.
Здесь JSFIDDLE, за которым следует код и кредиты, это должно быть очень легко следовать.
http://jsfiddle.net/BeyondLogical/evjzoo30/
--html -
<div ng-controller="FormCtrl">
<questions on-edit="editQuestion(ev,question)" ></questions>
</div>
- AngularJS -
var app = angular.module('myApp', []);
function FormCtrl ($scope) {
$scope.editQuestion = function (ev,question) {
//ev returns a MouseEvent object
alert("ev: " + ev);
//this is how you get the 'data' attribute set in the span tag below
alert("ev-data: " + ev.target.attributes.data.value);
};
}
app.directive('questions', function($compile) {
var tpl =
'<span ng-click="onEdit({ev: $event, myName: question})" data="This sentence would probably be replaced with a mustache brace parameter, example: {{someValue}}, returning a value from the scope." style="cursor:pointer;">Click Me</span>';
return {
restrict: 'E',
terminal: true,
scope: { onEdit: '&' },
template: tpl,
link: function(scope, element, attrs) {
$compile(element.contents())(scope.$new());
}
};
});
Кредиты,
Langdon - ng-click не работает в шаблоне директивы
Mark Rajcok - AngularJS получает $event из директивы (Langdon также получает помощь для ответа на вопрос Mark Answers)
PavanAsTechie - Значение атрибута доступа внутри функции неправляющего контроллера и Pavan JSFIDDLE - http://jsfiddle.net/brettdewoody/FAeJq/ (в частности, Павану, следуя строке кода): alert(obj.target.attributes.data.value);
Ответ 4
Чтобы кто-нибудь пришел к этому и попытался сделать это с кодом, который не работает в вашей директиве, убедитесь, что вы не используете опцию replace.
Например:
angular.module('app').directive('myDirective', function () {
return {
template: '<div ng-click="clicked()"></div>',
scope: {
options: "="
},
replace: true, //<---- Change this to false
restrict: 'E',
controller: function ($scope) {
$scope.clicked = function(){
console.log("Clicked");
}
}
};
}