56

我正在努力理解模型的范围及其与范围有限的指令的绑定。

我知道限制指令的范围意味着 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}}得到更新。为什么不是 template{{title}}{{data.title}}, 也不是 transclude {{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 个绑定?

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

4

3 回答 3

115

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

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

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

在此处输入图像描述

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

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

在此处输入图像描述

控制器范围 003 现在有一个新的data对象属性(这就是为什么它是黄色的),它title现在有一个属性设置为my title. 由于隔离范围属性title是单向数据绑定到 的内插值data.title,因此它也获取该值my title(该值显示为黄色,因为它已更改)。

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

隔离范围只能访问它自己的属性,因此只有 property 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: ...and部分,例如: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——它只会在控制器作用域中查找(即,它将遵循原型继承),而控制器作用域不会t 定义了这些属性。

于 2013-05-23T03:28:21.750 回答
24

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

范围继承

于 2013-05-24T12:52:38.043 回答
8

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

答案与嵌入元素如何获得其范围有关。

总而言之,您有两个范围:

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

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

您还拥有两个 HTML 部分,即嵌入和模板。发生的情况是,被嵌入的 HTML 在控制器的范围内,而模板 HTML 在指令的范围内。所以被嵌入的 HTML 不知道任何关于title的内容,模板范围也不知道关于data.title

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

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

我已经稍微更改了您的代码以使名称更清晰(但功能相同)

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

于 2013-05-20T16:14:38.683 回答