角度传递范围到 ng-include
Posted
技术标签:
【中文标题】角度传递范围到 ng-include【英文标题】:Angular passing scope to ng-include 【发布时间】:2014-10-29 23:24:24 【问题描述】:我有一个我写的控制器,我在我的应用程序的多个地方使用ng-include
和ng-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>
但这似乎不起作用。任何人都知道如何在这样的单一实例中为范围传递变量?
编辑:
上面的控制器正在加载leftItem
和rightItem
值,如下所示:
.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 要求两次引用您的模板名称,即。 <div ng-include-template="template-name.html" ... >
而不是 <div ng-include-template="'template-name.html'" ... >
.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 >
这是 Coffeescript,不是 Javascript。
这种方式更优雅,避免乱扔全局范围【参考方案6】:
我认为 ng-init 更适合这个。
<div ng-include='myFile.html' ng-init="myObject = myCtrl.myObject; myOtherObject=myCtrl.myOtherObject"/>
【讨论】:
请考虑编辑您的帖子,以添加更多关于您的代码的作用以及它为何能解决问题的说明。一个大部分只包含代码的答案(即使它正在工作)通常不会帮助 OP 理解他们的问题。 这不适用于使用不同变量多次绑定同一个模板【参考方案7】:上述内容不适用于二级属性,例如<div ng-include-template=... ng-include-variables=" id: var.id ">
。注意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的主要内容,如果未能解决你的问题,请参考以下文章