9

我正在尝试构建自定义指令,允许我在调查中显示问题。因为我有多种类型的问题,所以我考虑过创建单个指令并根据问题类型更改它的模板。

我的指令:

directive('question', function($compile) {
  var combo = '<div>COMBO - {{content.text}}</div>';
  var radio = [
    '<div>RADIO - {{content.text}}<br/>',
    '<md-radio-group layout="row" ng-model="content.answer">',
    '<md-radio-button ng-repeat="a in content.answers track by $index" ng-value="a.text" class="md-primary">{{a.text}}</md-radio-button>',
    '</md-radio-group>',
    '</div>'
  ].join('');
  var input = [
    '<div>INPUT - {{content.text}}<br/>',
    '<md-input-container>',
    '<input type="text" ng-model="content.answer" aria-label="{{content.text}}" required md-maxlength="10">',
    '</md-input-container>',
    '</div>'
  ].join('');

  var getTemplate = function(contentType) {
    var template = '';

    switch (contentType) {
      case 'combo':
        template = combo;
        break;
      case 'radio':
        template = radio;
        break;
      case 'input':
        template = input;
        break;
    }

    return template;
  }

  var linker = function(scope, element, attrs) {

    scope.$watch('content', function() {
      element.html(getTemplate(scope.content.type))
      $compile(element.contents())(scope);

    });
  }

  return {
    //require: ['^^?mdRadioGroup','^^?mdRadioButton'],
    restrict: "E",
    link: linker,
    scope: {
      content: '='
    }
  };
})

在我的主控制器中,我有问题列表,单击按钮后,我正在设置分配给我的指令的当前问题。

第一个问题一切正常,但在我将当前问题设置为无线电类型后,我收到此错误:

错误:找不到指令“mdRadioButton”所需的 [$compile:ctreq] 控制器“mdRadioGroup”!

我尝试按如下方式添加required到我的指令中,但没有帮助。

require: ['^mdRadioGroup'],

我不知道发生了什么,因为我还是 Angular 的新手。

我创建了 Plunker 来显示我的问题:http ://plnkr.co/edit/t0HJY51Mxg3wvvWrBQgv?p=preview

重现此错误的步骤:

  1. 打开 Plunker
  2. 单击Next按钮两次(导航到问题 3)
  3. 在控制台中查看错误

编辑:
我已经编辑了我的 Plunker,所以我的问题模型是可见的。我能够选择答案,即使是在抛出错误问题模型正在更新的问题中。但是在进行第 3 题时仍然出现错误。

4

4 回答 4

3

我只是简单地扩展一个基本指令,然后也有一个具有不同指令名称的专用指令。

// <div b></div>
ui.directive('a', ... )
myApp.directive('b', function(aDirective){
   return angular.extend({}, aDirective[0], { templateUrl: 'newTemplate.html' });
});

代码取自https://github.com/angular/angular.js/wiki/Understanding-Directives#specialized-the-directive-configuration

于 2016-02-19T11:57:26.943 回答
3

如果有人想知道,问题在于父组件的范围用于编译每个新元素。即使元素被删除,该范围上的绑定仍然存在(除非被覆盖),这可能会导致 OP 看到的错误(或者更糟糕的是,内存泄漏)。

这就是为什么在命令式地操作元素的 HTML 内容时应该注意清理的原因,就像这样。而且因为这很难做到正确,所以通常不鼓励这样做。大多数用例应该由内置指令覆盖(例如ngSwitch,对于 OP 的情况),这些指令会自行清理。


但是你可以在一个简化的场景中手动清理(就像这里的那个)。在最简单的形式中,它涉及为每个已编译的内容创建一个新的子范围,并在删除该内容后将其销毁。

这是修复 OP 的 plunker 所需要的:

scope.$watch('content', function () {
  element.html(getTemplate(scope.content.type));
  $compile(element.contents())(scope);
});

var childScope;
scope.$watch('content', function () {
  if (childScope) childScope.$destroy();
  childScope = scope.$new();
  element.html(getTemplate(scope.content.type));
  $compile(element.contents())(childScope);
});

这是固定版本

于 2018-09-13T09:47:45.890 回答
3

工作演示

无需为您的要求创建和使用指令。

您可以只使用角度模板和ng-include条件。

您可以像这样在页面上创建三个模板(每个模板用于组合、单选和输入),

<script type="text/ng-template" id="combo">
    <div>COMBO - {{content.text}}</div>
</script>

并将这些模板包含在一个 div 中,使用ng-include.

<!-- Include question template based on the question -->
<div ng-include="getQuestionTemplate(question)">

在这里,getQuestionTemplate()将返回应包含在此 div 中的模板的 id。

// return id of the template to be included on the html
$scope.getQuestionTemplate = function(content){
    if(content.type == "combo"){
      return 'combo';
    }
    else if (content.type == "radio"){
      return 'radio';
    }
    else{
      return 'input';
    }
}

就这样。你完成了。

请随时问我对此的任何疑问。

于 2016-03-01T04:52:02.987 回答
2

我对您的代码进行了一些尝试,发现发生错误的原因是因为第三个问题比第二个问题得到了更多的答案,所以当您第一次创建 mdRadioGroup 时,它定义了 4 个 $index 答案,后来又定义了第 3 个问题超出 6 个答案的范围......所以一个非优雅的解决方案是创建与任何问题的最大答案一样多的 $index,第一次,只显示带有文本的那些......

.directive('question', function($compile) {
var combo = '<div>COMBO - {{content.text}}</div>';
var radio = [
'<div>RADIO - {{content.text}}<br/>',
'<md-radio-group layout="row">',
'<md-radio-button ng-repeat="a in content.answers track by $index" ng-show={{a.text!=""}} value="{{a.text}}" class="md-primary">{{a.text}}</md-radio-button>',
'</md-radio-group>',
'</div>'
].join('');
var input = [
'<div>INPUT - {{content.text}}<br/>',
'<md-input-container>',
'<input type="text" ng-model="color" aria-label="{{content.text}}" required md-maxlength="10">',
'</md-input-container>',
'</div>'
].join('');

var getTemplate = function(contentType) {
var template = '';

switch (contentType) {
  case 'combo':
    template = combo;
    break;
  case 'radio':
    template = radio;
    break;
  case 'input':
    template = input;
    break;
}

return template;
}

然后更改问题以在所有问题中每次都有最大数量的答案:

$scope.questions = [{
type: 'radio',
text: 'Question 1',
answers: [{
  text: '1A'
}, {
  text: '1B'
}, {
  text: '1C'
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}]
}, {
type: 'input',
text: 'Question 2',
answers: [{
  text: '2A'
}, {
  text: '2B'
}, {
  text: '2C'
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}]
}, {
type: 'radio',
text: 'Question 3',
answers: [{
  text: '3A'
}, {
  text: '3B'
}, {
  text: '3C'
}, {
  text: '3D'
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}]
}, {
type: 'combo',
text: 'Question 4',
answers: [{
  text: '4A'
}, {
  text: '4B'
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}, {
  text: ''
}]
}];

其余代码相同。正如我之前所说,没有优雅的,肯定有更好的选择,但现在可能是一个解决方案......

于 2016-02-19T15:43:41.207 回答