被转换的内容需要transcluding指令的控制器

时间:2014-04-28 15:33:54

标签: javascript angularjs angularjs-directive

我有一个指令,正在抄袭它的内容。并且在被抄送的内容中是一个指令,它需要transcluding指令的控制器。如果我在transcluding指令中创建了一个transclude函数,则会引发错误。我认为这是因为当你提供一个transclude函数(https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L846)时,被克服的内容会被克隆。

我还有一个描述我问题的傻瓜:http://plnkr.co/edit/rRKWW6zfjZuUiw1BY4zs?p=preview

我想要做的是我想要转换内容并解析所有被转换的内容,然后将它放在DOM中的正确位置并自己编译。被抄送的内容实际上是我的指令的配置。

我还尝试清空我在转录函数中收到的克隆数组,因为我实际上并不需要自动转换内容。我只需要解析它并在稍后的时间点手动转换它。 Angular不需要对我的被抄送的内容做任何事情。但是这不起作用,因为在调用transcluding函数时已经识别出指令。所以当我清空数组时,我在这里收到错误(https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L961)。

亲切的问候,

大安

3 个答案:

答案 0 :(得分:9)

当您使用require: "^controller"时,您告诉Angular该指令要求在运行链接函数时将controller附加到祖先DOM元素。

当您在不使用ngTransclude指令的情况下进行转换时,您的父指令链接函数会传递一个transclude方法。 (你已经知道了;这只是为了完整性。)这个transclude方法执行以下操作:

  1. 提取内容以进行翻译
  2. 如果传入了cloneAttachFn,请克隆内容并调用cloneAttachFn
  3. 使用调用transclude提供的作用域调用$compile()来编译和链接内容(或克隆的内容)(默认为从指令作用域的$ parent继承的新作用域)。
  4. 如果您调用transclude,并且最终没有将内容附加为具有所需控制器的元素的后代(或者根本不将内容添加到DOM),则内容将没有父级所需的控制器。因为它无法找到所需的控制器,所以会出错。

    在您的示例中,如果将kmBar与require: "^kmFoo"一起使用,则限制将已转换的内容添加到DOM节点,这些节点是具有kmFoo的节点的后代。

    最简单的解决方法是继续将其附加到kmFoo的元素以用于$ compile()和链接,但立即detach

    Detach(与remove相对)维护点击处理程序等,因此如果稍后追加该元素,一切都将继续有效。如果您使用的是AngularJS的早期版本,则可能需要包含jQuery以进行分离,因为它不包含在早期版本的jqLite中。

    这是我放在一起的Plunk的片段

    app.directive('kmFoo', function() {
      return {
        restrict: 'A',
        scope: true,
        template: '<div></div>',
        transclude: true,
        controller: function() {
          // ...
        },
        link: function(scope, $element, attrs, ctrl, transcludeFn) {
          console.log('linking foo');
          // We are going to temporarily add it to $element so it can be linked,
          //  but after it's linked, we detach it.
          transcludeFn(scope, function(clone) {
            console.log('transcluding foo');
            $element.append(clone);
            c = clone;
          }).detach();// <- Immediately detach it
        }
      };
    });
    
    app.directive('kmBar', function() {
      return {
        restrict: 'A',
        scope: true,
        require: '^kmFoo',
        link: function(scope, $element, attrs, fooCtrl) {
          console.log('linking bar');
          // Right now it's a child of the element containing kmFoo,
          //  but it won't be after this method is complete.
          // You can defer adding this element to the DOM
          //  for as long as you want, and you can put it wherever you want.
        }
      };
    });
    

答案 1 :(得分:0)

首先,为什么需要转换功能?这还不够吗?

app.directive('kmFoo', function() {
  return {
    'restrict': 'A',
    'scope': true,
    'controller': function() {
      this.tryMe = function() { console.log("Success!") };
    },
    'link': function(scope, element, attrs, ctrl) {
      console.log('linking foo');
      var innerHtml = element.html();
      // do something with innerHtml
      element.html("<div>Empty</div>");
    }
  };
});

app.directive('kmBar', function() {
  return {
    'restrict': 'A',
    'scope': true,
    'require': '^kmFoo',
    'link': function(scope, element, attrs, fooCtrl) {
      fooCtrl.tryMe();
    }
  };
});

但是如果你真的想要访问fooController并在kmFoo中有一个transclude函数,你可以在完成所有链接并初始化所有控制器之后通过element.controller()访问控制器。

app.directive('kmFoo', function() {
  return {
    'restrict': 'A',
    'scope': true,
    'template': '<div ng-transclude></div>',
    'transclude': true,
    'controller': function() {
      this.tryMe = function() { console.log("Success!") };
    },
    'link': function(scope, $element, attrs, ctrl, transcludeFn) {
      console.log('linking foo');
      // when you put the transclude function in comments it won't throw an error
      transcludeFn(scope, function(clone) {
        console.log('transcluding foo');
      });
    }
  };
});

app.directive('kmBar', function() {
  return {
    'restrict': 'A',
    'scope': true,
    'template': "<button ng-click='tryMe()'>Feeling lucky?</button>",
    'link': function(scope, element, attrs) {
      scope.getFooCtrl = function() { 
        return element.parent().controller('kmFoo'); 
      };
      console.log('linking bar');
      console.log('parent not yet known: ' + element.parent().toString());
    },
    'controller': function($scope) {
      $scope.tryMe = function() {
        $scope.getFooCtrl().tryMe();
      };
    }
  };
});

使用this plnkr查看此内容。

答案 2 :(得分:0)

要在不将其显示在DOM中的情况下执行此操作,您可以使用

 transclude: 'element' 
第二条指令

这样可以避免使用一些技巧来获取所需的信息。

app.directive('kmFoo', function() {
  return {
    'restrict': 'A',
    'scope': true,
    'template': '<div ng-transclude></div>',
    'transclude': true,
    'controller': function() {

    },
    'link': function(scope, $element, attrs, ctrl, transcludeFn) {
      console.log('linking foo');
      // when you put the transclude function in comments it won't throw an error
      //transcludeFn(scope, function(clone) {
      //  console.log('transcluding foo');
      //});
    }
  };
});

app.directive('kmBar', function() {
  return {
    'restrict': 'A',
    'scope': {},
    'require': '^kmFoo',
    'link': function(scope, $element, attrs, fooCtrl) {
      console.log('linking bar');
    }
  };
});

app.directive('kmBarWithElement', function() {
  return {
    'restrict': 'A',
    'scope': {},
    'transclude': 'element',
    'require': '^kmFoo',
    'link': function(scope, $element, attrs, fooCtrl, transclude) {
      transclude(function(clone) {
        console.log('here the element: ', clone);
      });
    }
  };
});

以下是一个工作示例:http://plnkr.co/edit/lbT7oz74Yz7IZvEKp77T?p=preview