39

角菜鸟在这里。我正在创建一个指令来递归地显示问题树和子问题。我在模板中使用一个链接,它调用范围内的一个函数。出于某种原因,它不调用该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());
    }
  };
});

任何有助于理解这一点的帮助将不胜感激。

4

4 回答 4

38

你有一个范围问题。由于您在指令中使用了隔离范围 with 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());
    }
  };
});

请注意我们如何questionng-click. 这就是您在回调函数中定位参数的方式。另请注意,在on-edit我们传递给您的choices指令中,我们的目标是subQuestion. 这是因为question已经在 内部保留了ngRepeat,所以我们需要区分这两者。

到目前为止,这可能是我在 Angular 中最难学习的概念。一旦你理解了控制器、指令和其他指令之间的作用域,Angular 的世界就是你的了。:)

于 2013-05-10T19:29:18.863 回答
7

这是范围的问题。指令的 ng-click 调用当前作用域的 editQuestion 和 removeQuestion 方法,这些方法不存在于指令的作用域中,因为它们是在包含指令的模块中定义的(即父作用域)。

您想在指令和父级之间建立绑定,因此当指令调用 ngClick 函数时,它会在托管指令的模块上触发。

您可以在指令本身中定义方法,也可以通过指令定义对象的范围部分设置绑定

这是一个 plunker,说明了在不同范围内触发 ng-click 事件(输出到控制台)

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

于 2013-05-10T19:12:25.613 回答
2

兰登的 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/(特别是 Pavan 的以下代码行):alert(obj.target.attributes.data.value);

于 2015-09-14T19:51:51.823 回答
1

对于任何来此并尝试使用未在您的指令中运行的代码的人,请检查您是否未使用选项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");
      }
    }
  };
}
于 2016-02-23T03:50:08.230 回答