为什么Angular自定义指令范围影响父控制器范围?

时间:2016-07-18 10:15:14

标签: angularjs angularjs-directive

我创建了一个自定义指令,它显示了一个列表和选择框。

请按照以下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Template</title>

    <script type="text/javascript" src="/home/rahul/Installers/jquery-3.0.0.js"></script>   
    <link rel="stylesheet" type="text/css" href="/home/rahul/Installers/Bootstrapv3.0.2/css/bootstrap.css">
    <link rel="stylesheet" type="text/css" href="/home/rahul/Installers/Bootstrapv3.0.2/css/bootstrap-theme.css">
    <script type="text/javascript" src="/home/rahul/Installers/Bootstrapv3.0.2/js/bootstrap.js"></script>
    <script type="text/javascript" src="/home/rahul/Installers/angular.js"></script>

    <script type="text/javascript">
        angular.module("app",[]);
        angular.module("app").controller("myctrl",myctrl);
        angular.module("app").controller("childCtrl",childCtrl);
        angular.module("app").directive("loadFilms",loadFilms);

        myctrl.$inject = ["$scope"];
        childCtrl.$inject = ["$scope"];

        function myctrl($scope){
            var vm = this;
            vm.title = "Directive and nested scoping";
            vm.actorName = "Amithabh Bachhan";
            $scope.year = ["70's","80's","90's"];
        }

        function childCtrl($scope){
            var vm = this;

            vm.selectedYear = "";

            vm.moviesObj = {
                "70's" : ["Anand","Lawaris","Kala Pathhar","Deewar","Amar Akbar Anthony","Mili"],
                "80's" : ["Sharabi","Kaalia","Silsila","Satte Pe Satta","Nastik","Shahensha"],
                "90's" : ["Hum","Aaj Ka Arjun", "Ajooba","Khuda Gawah","Ganga Jamuna Saraswati","Lal Badshah"]
            }

            vm.movies = [];

            vm.getMovies = function(){
                vm.movies = vm.moviesObj[vm.selectedYear];
            }
        }

        function loadFilms(){
            return {
                restrict : "EA",
                controller : "childCtrl",
                controllerAs : "vm",
                scope : true,
                template : function(tElem, tAttrs){
                    var str = "<select ng-model='vm.selectedYear' data-ng-options='y as y for y in year' " + "class='form-control' ng-change='vm.getMovies()'>" + 
                    "<option value=''>Select</option>" + 
                    "</select>" +
                    "<br />" + 
                    "<ol class='slide-animate-container'>" + 
                    "   <li class='slide-animate' ng-repeat='m in vm.movies'>{{m}}</li>"+
                    "</ol>";
                    return str;
                }
            } 
        }
    </script>
</head>
<body>
    <div ng-app="app" ng-controller="myctrl as vm" class="container">
        <div class="page-header">
            <h3>{{vm.title}}</h3>
        </div>
        <div class="row">
            <div class="col-md-6">
                <div class="page-header">
                    <h5>{{vm.actorName}}</h5>                       
                </div>                      
            </div>
        </div>
        <div class="row">
            <div class="col-md-6" load-films></div> 
        </div>
    </div>
</body>     

指令“load-films”有自己的范围,

指令中选择框的值从封闭控制器“myctrl”的父作用域变量传递。

工作示例位于链接中 Example One

现在,我将指令“load-films”的范围更改为false

喜欢

 function loadFilms(){
            return {
                restrict : "EA",
                controller : "childCtrl",
                controllerAs : "vm",
                scope : false,
                template : function(tElem, tAttrs){
                    var str = "<select ng-model='vm.selectedYear' data-ng-options='y as y for y in year' " + "class='form-control' ng-change='vm.getMovies()'>" + 
                    "<option value=''>Select</option>" + 
                    "</select>" +
                    "<br />" + 
                    "<ol class='slide-animate-container'>" + 
                    "   <li class='slide-animate' ng-repeat='m in vm.movies'>{{m}}</li>"+
                    "</ol>";
                    return str;
                }
            } 
        }

父控制器的值vm.titlevm.actorName未在UI中加载。

为什么?

理想情况下,vm.titlevm.actorName位于控制器“myCtrl”中,因此如何将指令的scope属性设置为false会影响父控制器变量,如该 变量actorNametitle附加到this而不是$scope

使用{.., scope = false,..}的工作示例为here

3 个答案:

答案 0 :(得分:4)

您对vm使用相同的名称(controllerAs)。因此,变量被覆盖。

答案 1 :(得分:0)

删除指令中的controllerAs和范围。

controllerAs ,因为它强制视图中的 vm。成为指令控制器上属性的别名,而不是外部视图中定义的别名和范围,因为在将其设置为false时您不需要它。

然而,即使这会解决它,它也不是理想的方法。理想情况下,您应该保持 controllerAs 并使范围看起来像这样

scope : {
  title : "@"
}

然后更改您的视图以使用从

传入的标题
<blah load-films title="vm.title"/>

Directives with isolated scope是更好的方法,因此坚持使用 controllerAs 范围,但要确保您在父级上所需的值通过显式传递参数 - 这样您就不需要依赖包含指令的视图来设置具有特定别名的视图,例如&#34; vm&#34; (魔术字符串不好)

答案 2 :(得分:0)

LONG ANSWER:答案在于名称转换和初始化查询。 如果您调试正在初始化的内容,您将检测到:

  1. 首先是myctrl;
  2. 其次是childCtrl
  3. 如果您正在使用scope: true,则会应用后续步骤:

    1. $scope创建myctrl并指定$scope.vm = myctrlmyctrl as vm);
    2. $scope创建childCtrl并指定$scope.vm = childCtrlcontrollerAs: mv);
    3. vm.titlevm.actorName取自$scope.vm第一步。
    4. scope: false

      会发生什么
      1. $scope创建myctrl并指定$scope.vm = myctrlmyctrl as vm);
      2. 使用已创建的$scope myctrl,其中$scope.vm已存在;
      3. 初始化指令时 - 使用$scope.vmchildCtrl)重写controllerAs: 'vm';
      4. vm.titlevm.actorName取自$scope.vm第3步(来自childCtrl而不是myctrl)。
      5. 简短回答: 在每个控制器初始化之后,您仍然记得控制器与$scope.vm $scope.vm childCtrl而不是myctrl的{​​{1}} BUT 的关系,因为它是在指令初始化期间被覆盖并且$scope被共享。 vm.titlevm.actorName不属于childCtrl