角度传递范围到 ng-include

Posted

技术标签:

【中文标题】角度传递范围到 ng-include【英文标题】:Angular passing scope to ng-include 【发布时间】:2014-10-29 23:24:24 【问题描述】:

我有一个我写的控制器,我在我的应用程序的多个地方使用ng-includeng-repeat,像这样:

<div
  ng-repeat="item in items"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>

在控制器/模板中,我希望存在item 值,并且整个事情都是围绕这个想法构建的。不过,现在我需要以稍微不同的方式使用控制器,没有ng-repeat,但仍然需要能够传入item。我看到ng-init 并认为它可以做我需要的,像这样:

<div
  ng-init="item = leftItem"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>
<div
  ng-init="item = rightItem"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>

但这似乎不起作用。任何人都知道如何在这样的单一实例中为范围传递变量?

编辑: 上面的控制器正在加载leftItemrightItem 值,如下所示:

.controller('MainController', function($scope, ItemModel) 
    ItemModel.loadItems()
        .then(function(items) 
            $scope.$apply(function() 
                $scope.leftItem = items.left;
                $scope.rightItem = items.right;
            );
        );
);

【问题讨论】:

一个解决方案是创建一个新指令,正如我在 answer 中所说的那样 kbjr,请记住,ng-repeat 是一个创建新子作用域的指令,并且 ng-include 也是......除此之外,ng-init 可以与 ng- 结合使用包括...恐怕您必须显示控制器(ItemController 的代码)文件和模板文件(item.html)才能完全理解场景以及为什么不工作。 【参考方案1】:

聚会迟到了,但在不执行愚蠢指令的情况下,有一点角度“hack”来实现这一点。

添加一个内置指令,将在您使用 ng-include 的任何地方扩展控制器的范围(如 ng-if),实际上可以让您隔离所有包含范围的变量名称。

所以:

<div ng-include="'item.html'"
  ng-if="true"
  onload="item = rightItem">
</div>
<div ng-include="'item.html'"
  ng-if="true"
  onload="item = leftItem">
</div>

然后,您可以使用不同的项目将模板 item.html 绑定到 item 变量数次。

Here is a plunker to achieve what you want

问题是该项目在控制器范围内不断变化,该控制器范围仅包含一个对项目变量的引用,该变量在每个 onload 指令时都会被删除。

引入一个扩展当前范围的指令,让您有一个独立的范围用于所有 ng-include。 因此,项目引用在所有扩展范围内都被保留并且是唯一的。

【讨论】:

谢谢,这很好用。提示:我发现当添加一个在加载(异步)后动态填充的值时,它不起作用,因为在评估 include 语句时它不可用,但是将 if 语句更改为 ng-if="myVariable " 这样它只会在填充 myVariable 解决该问题时加载。 使用 onload 并不干净,因为它在全局范围内设置了一个变量。请看我的回答。 @Tanin 这就是我注入 ng-if 指令的原因,以便为每个 ng-include 创建一个唯一的隔离子范围并避免变量名冲突。【参考方案2】:

您可以使用ngInclude 提供的onload 属性来执行此操作:

<div ng-include="'item.html'"
     ng-controller="ItemController"
     onload="item = rightItem">
</div>

Link to the documentation.

编辑

尝试在父范围内做这样的事情:

$scope.dataHolder = ;

然后当接收到异步数据时,将数据存储在dataHolder

$scope.dataHolder.leftItem = items.leftItem;
$scope.dataHolder.rightItem = items.rightItem;

现在当ng-include 加载模板时,它将创建一个继承父级属性的子范围。所以$scope.dataHolder 将被定义在这个子作用域中(最初是一个空对象)。但是当接收到异步数据时,对空对象的引用应该包含新接收到的数据。

【讨论】:

这似乎也不起作用。我认为这可能与项目正在异步加载的事实有关,并且 onload 可能只运行一次? @kbjr 嗯,我刚刚在我的应用程序中对此进行了测试,它确实有效。我认为您关于异步加载项目的结论可能是解释。例如,如果我传入一个字符串值,它就可以工作。 这是我根据文档中的示例创建的plunkr。在“template1”中,我引用了一个名为 bar 的变量,该变量是从范围变量 foo 初始化的。 @kbjr 实际上在我的简单示例中甚至没有必要使用onload,因为ng-include 创建的范围继承了父范围的属性。因此,您可以通过在父作用域中初始化一个对象来解决这个问题,然后在加载异步数据时,更改您初始化的对象的属性。我将编辑我的答案... 我仍然需要在模板中引用这个新值,对吧?所以我没有在模板中使用item,而是使用dataHolder.item。当我需要使用 ng-repeat 时,这会破坏另一个用例。【参考方案3】:

我最终将它重写为一个指令并在范围内绑定所需的值

scope: 
    item: '='

【讨论】:

投了赞成票,因为 ng-init="" 和 onload="" 似乎不起作用,尽管它们应该......可惜我们每次都需要为这个简单的创建一个指令任务。 每次都写一个特定的指令并不理想。请看我的解决方案。希望对您有所帮助!【参考方案4】:

喜欢@Tanin 的回答。以一种非常优雅的方式一次解决了so许多问题。对于像我这样不了解 Coffeescript 的人,这里是 javascript...

注意:由于我太新而无法理解,此代码要求您引用您的模板名称​​一次,而不是 ng-include 要求两次引用您的模板名称,即。 &lt;div ng-include-template="template-name.html" ... &gt; 而不是 &lt;div ng-include-template="'template-name.html'" ... &gt;

.directive('ngIncludeTemplate', function()   
  return   
    templateUrl: function(elem, attrs)  return attrs.ngIncludeTemplate; ,  
    restrict: 'A',  
    scope:   
      'ngIncludeVariables': '&'  
    ,  
    link: function(scope, elem, attrs)   
      var vars = scope.ngIncludeVariables();  
      Object.keys(vars).forEach(function(key)   
        scope[key] = vars[key];  
      );  
      
    
)

【讨论】:

【参考方案5】:

使用 onload 不是一个干净的解决方案,因为它会在全局范围内乱扔垃圾。如果你有更复杂的东西,它就会开始失败。

ng-include 不是那么可重用,因为它可以访问全局范围。有点奇怪。

以上说法不正确。带有 onload 的 ng-if 不会乱扔全局范围

我们也不想为每种情况编写一个特定的指令。

使用通用指令代替 ng-include 是一种更简洁的解决方案。

理想的用法如下:

<div ng-include-template="'item.html'" ng-include-variables=" item: 'whatever' "></div>
<div ng-include-template="'item.html'" ng-include-variables=" item: variableWorksToo "></div>

指令是:

.directive(
  'ngIncludeTemplate'
  () ->
    
      templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
      restrict: 'A'
      scope: 
        'ngIncludeVariables': '&'
      
      link: (scope, elem, attrs) ->
        vars = scope.ngIncludeVariables()
        for key, value of vars
          scope[key] = value
    
)

您可以看到该指令不使用全局范围。相反,它从 ng-include-variables 读取对象并将这些成员添加到自己的本地范围内。

我希望这是你想要的;它干净而通用。

【讨论】:

Uncaught SyntaxError: Unexpected token &gt; 这是 Coffeescript,不是 Javascript。 这种方式更优雅,避免乱扔全局范围【参考方案6】: 我认为

ng-init 更适合这个。

<div ng-include='myFile.html' ng-init="myObject = myCtrl.myObject; myOtherObject=myCtrl.myOtherObject"/>

【讨论】:

请考虑编辑您的帖子,以添加更多关于您的代码的作用以及它为何能解决问题的说明。一个大部分只包含代码的答案(即使它正在工作)通常不会帮助 OP 理解他们的问题。 这不适用于使用不同变量多次绑定同一个模板【参考方案7】:

上述内容不适用于二级属性,例如&lt;div ng-include-template=... ng-include-variables=" id: var.id "&gt;。注意var.id

更新指令(丑陋,但有效):

.directive('ngIncludeTemplate', function() 
  return 
    templateUrl: function(elem, attrs)  return attrs.ngIncludeTemplate; ,
    restrict: 'A',
    scope: 
      'ngIncludeVariables': '&'
    ,
    link: function(scope, elem, attrs) 
      var cache = scope.ngIncludeVariables();
      Object.keys(cache).forEach(function(key) 
        scope[key] = cache[key];
      );

      scope.$watch(
        function() 
          var val = scope.ngIncludeVariables();
          if (angular.equals(val, cache)) 
            return cache;
          
          cache = val;
          return val;
        ,
        function(newValue, oldValue) 
          if (!angular.equals(newValue, oldValue)) 
            Object.keys(newValue).forEach(function(key) 
              scope[key] = newValue[key];
            );
          
        
      );

    
  ;
);

【讨论】:

这应该是 ngInclude 本身的一部分! ng-include 是 Angular 中模块化的基本单元,它应该有一个干净的参数化方式。 此解决方案负责处理第一个解决方案没有的模型更改...很棒!【参考方案8】:

可能对 Mike 和 Tanin 的答案有明显的更新 - 如果您使用内联模板:

<script type="text/ng-template" id="partial">variable</script>

<div ng-include-template="'partial'" ng-include-variables="variable: variable"></div>

然后,在指令 ngIncludeTemplate 中,替换

templateUrl: function(elem, attrs)  return attrs.ngIncludeTemplate; ,  

template: function(elem, attrs)  return document.getElementById(attrs.ngIncludeTemplate.split("'")[1]).innerHTML ,

【讨论】:

以上是关于角度传递范围到 ng-include的主要内容,如果未能解决你的问题,请参考以下文章

角度 1.5。如何使嵌入的模板绑定到组件范围

如何将一些字符串或数据从 API 传递到角度 6 中的角度组件?

markdown 角度 - 将组件传递到组件中

角度传递数组到另一个(非子)组件

typescript 角度传递字符串到子组件输入

如何通过传递对象使用 [ngstyle] 将网格行和网格列动态传递到角度 div 选项卡