【问题标题】:Adding $properties to Transcluded Scope将 $properties 添加到 Transcluded 范围
【发布时间】:2015-03-18 18:37:38
【问题描述】:

我有一个嵌入范围的指令,如下所示:

<my-control>
    Some Content: {{value}}
</my-control>

value 来自父作用域。

我想添加一个与控件范围交互的函数,所以我可以这样做:

<my-control>
    Some Content: {{value}}
    <button ng-click="$close()">Close</button>
</my-control>

类似于ngRepeat$index 等属性添加到行范围的方式。在我的指令中执行此操作的最简单方法是什么?

【问题讨论】:

  • 嘿,保罗!像这样的东西对你有用吗? plnkr.co/edit/SPSFGcB49qXmXROmNeHj?p=preview
  • @sergiocruz,我需要该指令有一个独立的范围,请参阅接受的答案。
  • True...好吧,我只想指出,如果将scope: {} 添加到指令声明中,最终结果是相同的。加上一点清洁恕我直言:)

标签: angularjs transclusion


【解决方案1】:

您可以在指令链接函数中将新方法附加到您的作用域(如果您有的话,甚至可以在指令的控制器中)。

为了简单起见,我将在这里展示如何将新方法附加到指令的链接函数:

app.directive('myControl', function() {
    return {
        restrict: 'E',
        transclude: true,
        template: '<div ng-transclude></div>',
        link: function postLink(scope) {
            scope.$close = function close() {
                console.log("Close function that lives in directive...");
            };
        }
    };
});

在您的 HTML 中,您应该能够简单地调用该函数:

<my-control>
    Click <a href ng-click="$close();">close</a> things.
</mycontrol>

还想通过上面的例子来看看这个 plunker 在实践中的工作: http://plnkr.co/edit/SPSFGcB49qXmXROmNeHj?p=preview

我希望这会有所帮助,如果我遗漏了任何内容,请随时告诉我,我很乐意提供任何其他信息。

【讨论】:

  • 如果你要添加自己的方法,你应该避免使用'$'作为前缀以符合AngularJS的约定。
  • 嘿,史蒂夫,我同意,我只是想与保罗的问题保持一致,所以我不会混淆他(他问的是 $close 方法)。
  • 另外值得一提的是,像 angular-ui 这样的优秀库确实会在其服务前加上 $。
  • 只是把它放在那里。
  • 这只是因为指令没有声明它自己的范围,而是将方法附加到指令和嵌入内容都可以访问的继承范围。问题在于这也意味着 $close 方法可以在指令范围之外访问。在同一个父作用域中包含其中两个指令,$close 方法将被覆盖。
【解决方案2】:

当我们没有指定 scope:true(new Scope) 或 scope:{} (isolatedScope) 时,当我们重新使用该指令时,在作用域上定义的属性将被覆盖。

例如:

<div ng-controller="AppCtrl">
    <my-control name="myControl1">
        Some Content: {{value}} 
        My Control Name: {{name}}
    </my-control>
    <my-control name="myControl2">
        Some Content: {{value}} 
        My Control Name: {{name}}
    </my-control>
</div>

它不会在屏幕上同时打印myControl1myControl2,而是打印两次myControl2

Plnkr

要解决此问题,请尝试以下任一解决方案。

解决方案1

transclde:true 将创建一个新的作用域。在这个范围而不是指令的范围上设置属性。

app.directive('myControl', function() { 
  return {
    restrict: 'E',
    transclude: true,
    template: '<div><p><strong>Welcome to the testMe directive!</strong></p> <div ng-transclude></div></div>',
    link: function(scope, element, attrs) {
      var transclusionTarget = element[0].querySelector('[ng-transclude]').firstChild;
      var transclusionScope = angular.element(transclusionTarget).scope();
      transclusionScope.name = attrs.name;
    }
  }
});

这里ng-transclude div 下的元素将使用transclusionScope 进行编译,抓取它并更新其中的属性。

Plnkr

解决方案2 不要使用ng-transclude,而是手动嵌入内容。

app.directive('myControl', function() {  
  return {
    restrict: 'E',
    transclude: true,
    template: '<div><p><strong>Welcome to the testMe directive!</strong></p> <div transclude-target></div></div>',
    link: function(scope, element, attrs, directiveCtrl, transcludeFn ) {

      var transclusionScope = scope.$new(),
          transclusionTarget = element[0].querySelector('[transclude-target]');

      transclusionScope.name = attrs.name;

      transcludeFn(transclusionScope, function (clone) {
        angular.element(transclusionTarget).append(clone);
      });
    }
  }
});

在这里,创建一个new Scope 使用scope.$new() 扩展指令的范围。并更新其中的属性。

Plnkr

Solution1 可能并不适用于所有情况。当我们访问firstChild 时,如果它还没有准备好Solution1 将失败

Solution2 更简洁,适用于所有情况。

【讨论】:

  • 非常感谢,解决方案 2 成功了。实际上已经尝试过类似的事情,但是当我想要做的只是修改范围并且我很确定我做错了时,它似乎有点令人费解地弄乱了 DOM。我想这确实是正确且唯一的选择。
【解决方案3】:

Vinay 的回答是正确的,但我会对其进行修改以使其更“有角度”

公开指令 API 的“角度”方式是通过控制器。我会遵循 ngForm 指令使用的模式 -

类似这样的:

app.directive('myControl', function() {  
  return {
    restrict: 'E',
    transclude: true,
    controller: function($scope) {
        this.$close = function(){
            //close me
        }
        this.$open = function() {
            //open me
        }

    }
    template: '<div><p><strong>Welcome to the testMe directive!</strong></p> <div transclude-target></div></div>',
    link: function(scope, element, attrs, directiveCtrl, transcludeFn ) {

      transcludeFn(scope.$new(), function (clone, transclusionScope) {
         //only expose the API to the scope if the name attribute is present
          if(attrs.name) {
              transclusionScope[name] = directiveCtrl;
          }
          angular.element(element[0].querySelector('[transclude-target]').append(clone);
      });
    }
  }
});

配合用法:

 <my-control name="myControl2">
    <button ng-click="myControl2.$close()>Close</button>
 </my-control>

【讨论】:

  • 我认为在我的情况下使用控件的名称将大大增加与父范围发生冲突的几率。加上 ngRepeat 直接将 $index 等属性添加到作用域中,因此有一个“官方”API 的示例以另一种方式工作。
  • 使用“name”属性的意义在于,它让您可以控制如何将 API 添加到作用域中——因此,如果发生冲突,您只需更改名称即可。在这种情况下发生碰撞的几率为零。我也不认为直接向子范围添加方法(或属性)是“错误的”。根据经验,我刚刚发现我的建议效果很好,尤其是当指令的 API 具有多个方法或属性时。
猜你喜欢
  • 2015-07-27
  • 2010-12-01
  • 1970-01-01
  • 2022-11-03
  • 2013-10-03
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多