ng-click не работает в шаблоне директивы

Угловой нуб здесь. Я создаю директиву для рекурсивного отображения дерева вопросов и подвопросов. Я использую ссылку в шаблоне, которая вызывает функцию в рамках. По какой-то причине он не вызывает метод 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());
    }
  };
});

Любая помощь в понимании этого будет оценена по достоинству.


person Madhusudhan    schedule 10.05.2013    source источник


Ответы (4)


У вас проблема с масштабом. Поскольку вы использовали изолированную область в своей директиве с 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 станет вашим. :)

person Langdon    schedule 10.05.2013
comment
Спасибо. Я ошибся, добавив в директиву ng-click=onEdit(). - person Aravind; 13.07.2015
comment
@Langdon спасибо, что поделились. Это помогло мне решить проблему, с которой я столкнулся. В моем шаблоне у меня была ‹button ng-click=doSomethingInMainCtrl({{ twowaybound.value}} )›Button в директиве ‹/button›, и она продолжала выдавать ошибки парсера, пока я не удалил фигурные скобки вокруг twowaybound.value после просмотра вашего примера . - person Dean Sha; 15.04.2017

Это проблема масштаба. Директива ng-click вызывает методы editQuestion и removeQuestion текущей области, которые не существуют в области действия директивы, поскольку они определены в модуле, который включает директиву (т.е. родительскую область).

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

Либо вы можете определить методы в самой директиве, либо настроить привязку через раздел области видимости объекта определения директивы

Вот плункер, который иллюстрирует запуск событий ng-click в разных областях (выводит на консоль)

http://plnkr.co/edit/9XfXCpU6lhUOqD6nbVuQ?p=preview

person Jason    schedule 10.05.2013

Ответ Лэнгдона от 10 мая 13 верен. В демонстрационных целях я упростил код скрипта Лэнгдона и сократил его со 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());
        }
    };
});

Кредиты,
Лэнгдон - ng-click не работает в шаблоне директивы

Марк Райкок - AngularJS получает событие $event из директивы (Лэнгдон также получает помощь, задавая вопрос Отметить ответы)

PavanAsTechie - Доступ к значению атрибута внутри функции недирективного контроллера и JSFIDDLE от Pavan - http://jsfiddle.net/brettdewoody/FAeJq/ (особенно следующая строка кода Павана):alert(obj.target.attributes.data.value);

person user3777549    schedule 14.09.2015

Всем, кто придет к этому и попытается сделать это с кодом, который не работает в вашей директиве, убедитесь, что вы не используете параметр replace< /сильный>.

Eg:

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");
      }
    }
  };
}
person Ryan Knell    schedule 23.02.2016