【问题标题】:Confused about Angularjs transcluded and isolate scopes & bindings对 Angularjs 的嵌入和隔离范围和绑定感到困惑
【发布时间】:2013-05-15 05:12:08
【问题描述】:

对于范围有限的指令,我很难理解模型的范围及其绑定。

我知道限制指令的范围意味着 controller.$scope 和directive.scope 不再是同一个东西。但是,我对在指令模板或 html 中放置模型如何影响数据绑定感到困惑。我觉得我错过了一些非常基本的东西,要继续前进,我需要了解这一点。

获取以下代码(此处为小提琴:http://jsfiddle.net/2ams6/

JavaScript

var app = angular.module('app',[]);
app.controller('Ctrl',function($scope){
});
app.directive('testel', function(){
    return {
        restrict: 'E',
        scope: {
            title: '@'
        },
        transclude: true,
        template:   '<div ng-transclude>'+
                    '<h3>Template title: {{title}}</h3>' +
                    '<h3>Template data.title:{{data.title}}</h3>' +
                    '</div>'
    }    
}); 

HTML

<div ng-app='app'>
    <div ng-controller="Ctrl">
        <input ng-model="data.title">
        <testel title="{{data.title}}">
            <h3>Transclude title:{{title}}</span></h3>
            <h3>Transclude data.title:{{data.title}}</h3>
        </testel>
    </div>
</div>

模型仅在模板内更新{{title}},在嵌入中更新{{data.title}}为什么不是{{title}} 在嵌入中,也不是{{data.title}} 在模板中?

像这样将输入移动到嵌入中(这里小提琴:http://jsfiddle.net/eV8q8/1/):

<div ng-controller="Ctrl">
    <testel title="{{data.title}}">
        <input ng-model="data.title">
         <h3>Transclude title: <span style="color:red">{{title}}</span></h3>

         <h3>Transclude data.title: <span style="color:red">{{data.title}}</span></h3>

    </testel>
</div>

现在意味着只有 transclude {{data:title}} 被更新。 为什么不使用{{title}}{{data.title}} 模板,也不要转换{{title}}

最后,将输入移动到模板中,就像这样(在这里摆弄:http://jsfiddle.net/4ngmf/2/):

template: '<div ng-transclude>' +
            '<input ng-model="data.title" />' +
            '<h3>Template title: {{title}}</h3>' +
            '<h3>Template data.title: {{data.title}}</h3>' +
            '</div>'

现在意味着只有模板{{data.title}} 得到更新。 再说一遍,为什么不是其他 3 个绑定?

我希望有什么明显的东西在盯着我看,但我很想念它。如果你让我得到这个,我会给你买啤酒,或者给你一些积分,或者其他类似的东西。非常感谢。

【问题讨论】:

  • 这是一个很大的话题,很遗憾在开始工作之前我没有时间提供太多细节,但请阅读 Mark Rajcok 的 Scope/Prototypal Inheritance Guide。有很多图表,这很有帮助,他涵盖了嵌入。
  • @BrandonTilley 惊人的文章。非常感谢您发布它。只是努力解决它。周二可以花更多的时间。

标签: angularjs angularjs-directive angularjs-scope


【解决方案1】:

您的小提琴创建了三个范围:

  1. 与控制器 Ctrl 关联的范围,因为 ng-controller
  2. 由于transclude: true,一个指令嵌入范围
  3. 一个指令隔离范围,因为scope: { ... }

在 fiddle1 中,在我们在文本框中输入任何内容之前,我们有以下内容:

Scope 003 是与控制器关联的范围。由于我们还没有在文本框中输入内容,因此没有 data 属性。在隔离范围 004 中,我们看到创建了一个 title 属性,但它是空的。它是空的,因为父作用域还没有 data.title 属性。

在文本框中输入my title 后,我们现在有:

控制器作用域 003 现在有一个新的 data 对象属性(这就是它显示为黄色的原因),它的 title 属性现在设置为 my title。由于隔离作用域属性title 是单向数据绑定到data.title 的插值值,因此它也获得了值my title(该值被涂成黄色,因为它发生了变化)。

transcluded 作用域原型继承自控制器作用域,因此在transcluded HTML 内部,angular 可以跟随原型链并在父作用域中找到$scope.data.title(但$scope.title 不存在那里)。

隔离作用域只能访问它自己的属性,因此只有属性title

在 fiddle2 中,在打字之前我们有与 fiddle1 相同的图片。

输入my title后:

注意新的data.title 属性出现在哪里——在嵌入的范围内。隔离作用域仍在控制器作用域上寻找data.title,但这次不存在,因此其title 属性值保持为空。

在 fiddle3 中,在打字之前我们有与 fiddle1 相同的图片。

输入my title后:

注意新的data.title 属性出现在哪里——在隔离范围内。其他范围都无法访问隔离范围,因此字符串 my title 不会出现在其他任何地方。


Angular v1.2 更新:

随着 eed299a 的更改,Angular 现在会在嵌入之前清除嵌入点,因此 Template title: ...Template data.title: ... 部分不会显示,除非您修改模板以使 ng-transclude 单独存在,例如:

'<h3>Template title: <span style="color:red">{{title}}</span></h3>' +
'<h3>Template data.title: <span style="color:red">{{data.title}}</span></h3>' +
'<div ng-transclude></div>'

在下面的 Angular v1.3 更新中,进行了此模板更改。


Angular v1.3+ 的更新:

从 Angular v1.3 开始,transcluded 作用域现在是指令的隔离作用域的子作用域,而不是控制器作用域的子作用域。所以在 fiddle1 中,在我们输入任何内容之前:

本次更新中的图片是使用Peri$scope工具绘制的,所以图片略有不同。 @ 表示我们有一个使用 @ 语法的隔离范围属性,粉红色背景表示该工具无法找到映射的祖先引用(这是真的,因为我们没有在其中输入任何内容到文本框)。

在文本框中输入my title 后,我们现在有:

使用@ 绑定的隔离属性将始终在隔离范围内的@ 符号之后显示内插字符串结果。 Peri$scope 还能够在祖先作用域中找到这个确切的字符串值,因此它还显示了对该属性的引用。

在小提琴 2 中,在打字之前我们有与 fiddle1 相同的图片。

输入my title后:

注意新的data.title 属性出现在哪里——在嵌入的范围内。隔离作用域仍在控制器作用域上寻找data.title,但这次不存在,因此其title 属性值保持为空。

在 fiddle3 中,在打字之前我们有与 fiddle1 相同的图片。

输入my title后:

注意新的data.title 属性出现在哪里——在隔离范围内。即使被嵌入的作用域可以通过$parent 关系访问隔离作用域,它也不会在那里寻找titledata.title——它只会在控制器作用域中查找(即,它将遵循原型继承),并且控制器范围没有定义这些属性。

【讨论】:

  • @Mark 你是怎么做这些图表的?有工具吗?
  • @mikea,我几个月前写了一个半生不熟的工具。它一开始很干净,但是当我开始尝试添加越来越多的用例和功能时,它很快就变得老套了。一个指令解析大量 Angular 对象(一些代码从 Batarang 中重用),然后将有趣的信息发送到 Python 脚本,该脚本生成一个 GraphViz 点文件,该文件在服务器端呈现,然后作为图像返回给浏览器。
  • @mikea,我烘焙了工具的另一半,并以Peri$scope的名义放在github上
  • @MarkRajcok 很遗憾您的 Peri$scope 项目似乎已被放弃,我们可以使用这样的工具来调试我们的应用程序。我试图用它来调试我们的 AngularJS 1.6 应用程序,但我得到了错误。尽管如此,伟大的工作! ;)
【解决方案2】:

在阅读了所有提供的答案(包括 Mark 的精彩示意图)之后,这是我对范围的理解,并且它是我提出的问题的继承。我希望 cmets 知道这张图在哪里下降,以便我可以适当地更新。我希望它只是提供与 Mark 所呈现内容不同的观点:

【讨论】:

  • 请注意,您可以change how the scopes are created,例如将指令的范围暴露给它被嵌入的子代。
  • @z0r 非常有帮助,谢谢。关于角度的文档过去非常有限。看起来情况正在好转! :)
【解决方案3】:

问得好,顺便说一句!希望我的回答一样有说服力..

答案与被嵌入的元素如何获得它们的范围有关。

总而言之,您有两个作用域:

  1. 控制器的作用域,有$scope.data.title。 (由您的 input 元素隐式添加)
  2. 指令的范围,有$scope.title

当您更改控制器的$scope.data.title 时,指令的$scope.title 也会更改。

您还拥有两个 HTML 部分,即嵌入部分和模板部分。发生的情况是被嵌入的 HTML 在 controller 的 范围内,而模板 HTML 在 directive 的 范围内。所以被转入的 HTML 对title 一无所知,模板作用域对data.title 一无所知

这实际上正是 Transclusion 的目的 - 允许指令的子元素保持其父范围,在这种情况下是控制器的范围。按照设计,被嵌入的元素不知道它们在指令中,因此无法访问指令的范围。

另一方面,指令模板只能访问指令的范围。

我对您的代码进行了一些更改,以使名称更加清晰(但功能相同)

http://jsfiddle.net/yWWVs/2/

【讨论】:

  • 感谢您花时间回答。我觉得我正在从浑浊的水中走出来,变得更清澈。 Brandon Tilley 上面发布的文章也非常有帮助,因此我将它与您的答案一起消化,希望一切都会到位。我想当我进入 Brandon 文章的嵌入部分时,事情会清楚得多。我会花时间非常缓慢而刻意地消化答案,所以请耐心等待。
猜你喜欢
  • 1970-01-01
  • 2014-07-08
  • 2017-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-17
  • 1970-01-01
  • 2017-07-13
相关资源
最近更新 更多