当标记位于单个文件中时,为什么我的嵌套指令不起作用?

Why don't my nested directives work when the markup is in a single file?

我一直在研究 isolateScope 指令,以更好地了解它们如何与其他嵌套的 isolateScope 指令交互,因此将 plnkr 放在一起来测试一些东西。

http://plnkr.co/edit/7Tl7GbWIovDSmVeKKN26?p=preview

这按预期工作。如您所见,每个指令都有自己独立的模板。

然后我决定将 html 从每个指令中移出并移到主 html 文件中,但是它停止工作了?我可以看到 e1Ctrl 在指令的范围内,但在处理包含的标记时它似乎不可用。

http://plnkr.co/edit/33Zz1oO4q7BVFw0cMvYa?p=preview

谁能告诉我为什么会这样?

------------更新------------

我简化了无法正常工作的 plunker 以清楚地显示问题。该指令使用 controllerAs 语法并且 e1Ctrl 明确设置在其 $scope 上(请参阅控制台输出)。

http://plnkr.co/edit/g2U2XskJDwWKuK3gqips?p=preview

angular
  .module('app', [])
  .controller('AppCtrl', AppCtrl)
  .directive('elementOne', elementOne)
  .controller('E1Ctrl', E1Ctrl)


function AppCtrl() {

  var vm = this;

  vm.data = [
  {
    label: 'one'
  },
  {
    label: 'two'
  },
  {
    label: 'three'
  },
  {
    label: 'four'
  }
  ];

  vm.callback = function() {
    console.log('called app callback');
  };
}


function elementOne() {
  return {
    restrict: 'E',
    scope: {
      data: '=',
      handler: '&'
    },
    controller: 'E1Ctrl',
    controllerAs: 'e1Ctrl',
    bindToController: true
  }
}

function E1Ctrl($scope) {
  console.log('E1Ctrl', $scope);
  var vm = this;

  vm.click = function() {
    vm.handler();
  };

  vm.callback = function() {
    console.log('called e1 callback');
  };
}

加价:

<body ng-app="app" ng-controller="AppCtrl as appCtrl">
  <ul>
    <div ng-repeat='item in appCtrl.data'>

      <element-one data='item' handler='appCtrl.callback()'>
        <button ng-click='e1Ctrl.click()'>e1: {{e1Ctrl.data.label}}</button>
      </element-one>

    </div>
  </ul>
</body>

------ 嵌入解决方案 -----

http://plnkr.co/edit/l3YvnKOYoNANteNXqRrA?p=preview

function elementOne() {
  return {
    restrict: 'E',
    transclude: true,
    scope: {
      data: '=',
      handler: '&'
    },
    controller: 'E1Ctrl',
    link: function($scope, $element, $attr, ctrl, transclude) {

      transclude($scope, function(clone){
        $element.append(clone);
      });
    }
  }
}

指令的 template 中的 HTML 和指令子树中的 HTML 的范围有所不同。前者在指令范围的上下文中进行评估;后者 - 在视图范围内。

如果指令有一个隔离作用域 - scope: {},则子树看不到它。如果它使用 scope: true,那么它会为子树创建一个新的子范围,该子树的原型继承自视图的范围。

考虑以下几点:

// isolate scope
app.directive("foo", function(){
  return {
    scope: {},
    link: function(scope){
      scope.name = "foo";
    }
  }
});

// child scope
app.directive("bar", function(){
  return {
    scope: true,
    link: function(scope){
      scope.name = "bar";
    }
  }
});

app.controller("Main", function($scope){
  $scope.name = "main";
});

视图的呈现方式如下:

<body ng-controller="MainCtrl">
  <pre>in main: {{name}} will render "main"</pre>
  <foo>
    <pre>in subtree of foo: {{name}} will render "main"</pre>
  </foo>
  <bar>
    <pre>in subtree of bar: {{name}} will render "bar"</pre>
  </bar>
</body>

在您的情况下,子树是在视图的范围内计算的 - 而不是指令,这就是它无法按预期工作的原因。

plunker

编辑:

在某些情况下,在指令的隔离范围的上下文中评估子树可能是有意义的。我已经看到它与允许模板的指令一起使用。但要小心这一点,因为主视图的作者不应该(太多)了解指令的内部工作原理(即内部范围中公开的内容)。这也很难阅读,因为您会看到在外部范围内没有意义的变量。

要在指令的隔离范围内评估子树,指令需要 $compile 子树并 link 它针对其范围。

这是一个允许用户为列表中的每个项目提供模板的指令。 item 变量未在主作用域中定义,仅在指令的隔离作用域的上下文中才有意义:

<list src="items">
  <item-template>
    {{item.a}} | {{item.b}}
  </item-template>
</list>

指令'list'如下:

app.directive("list", function($compile){
  return {
    scope: {
      src: "="
    },
    link: {
      pre: function(scope, element){
        var itemTemplate = element.find("item-template");
        element.empty();

        var template = angular.element('<div ng-repeat="item in src"></div>')
                              .append(itemTemplate.html());

        element.append(template);
        $compile(element.contents())(scope);
      }
    }
  }
});

plunker 2