【问题标题】:Why Angular custom directive scope effects parent controller scope?为什么 Angular 自定义指令范围会影响父控制器范围?
【发布时间】:2016-07-18 10:15:14
【问题描述】:

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

请按照下面的代码,

<!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

【问题讨论】:

  • scope:false 更改为scope: {}

标签: angularjs angularjs-directive


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    长答案:答案在于名称转换和初始化查询。 如果您调试正在初始化的内容,您会发现:

    1. 第一个是myctrl;
    2. 第二个是childCtrl

    如果您使用的是scope: true,则应用下一步:

    1. myctrl创建$scope并分配$scope.vm = myctrlmyctrl as vm);
    2. childCtrl创建$scope并分配$scope.vm = childCtrlcontrollerAs: mv);
    3. vm.titlevm.actorName 取自第一步中的 $scope.vm

    如果scope: false: 会发生什么

    1. myctrl创建$scope并分配$scope.vm = myctrlmyctrl as vm);
    2. 将已创建的$scope 用于myctrl,其中$scope.vm 已存在;
    3. 初始化指令时 - 用childCtrl (controllerAs: 'vm') 重写$scope.vm
    4. vm.titlevm.actorName 取自第三步的 $scope.vm(取自 childCtrl 而不是 myctrl)。

    简短回答: 初始化每个控制器后,您仍然记得控制器与 $scope.vm$scope.vm 的关系 您有 childCtrl 而不是 myctrl,因为它在指令初始化期间被覆盖,$scope是共享的。而vm.titlevm.actorName 不是childCtrl 的一部分。

    【讨论】:

      【解决方案3】:

      在你的指令中去掉 controllerAs 和 scope。

      controllerAs 因为它强制您视图中的 vm. 成为指令控制器上属性的别名,而不是外部视图中定义的别名,并且 scope 因为设置为 false 时不需要它。

      但是,即使这样可以解决问题,它也不是理想的方法。理想情况下,您应该保持 controllerAs 并让 scope 看起来像这样

      scope : {
        title : "@"
      }
      

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

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

      Directives with isolated scope 是更好的选择,因此请坚持使用 controllerAsscope,但请确保通过参数显式传入您想要的父级值 -这样您就不需要依赖包含指令的视图来设置具有特定别名的视图,例如“vm”(魔术字符串不好)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-09
        • 1970-01-01
        • 1970-01-01
        • 2013-05-28
        • 1970-01-01
        相关资源
        最近更新 更多