为什么'controller as'无法直接从子控制器访问父作用域的属性?

时间:2016-11-22 16:05:39

标签: javascript angularjs controller

在阅读一些关于它的文章后,我打算将我的代码转换为使用controller as语法。但是,我很惊讶使用controller as语法,不能再直接使用父控制器的变量,我的意思是,用旧方式(使用$ scope),我可以这样做:

// <div ng-controller="ParentCtrl">
//   <div ng-controller="ChildCtrl"></div>
// </div>
// 
app.controller('ParentCtrl', function ($scope) {
  $scope.x.title = 'Some title';
});


app.controller('ChildCtrl', function ($scope) {
  console.log($scope.x.title);
});

但是controller as,我必须这样做(感谢question}):

// <div ng-controller="ParentCtrl as pc">
//   <div ng-controller="ChildCtrl as cc"></div>
// </div>
// 
app.controller('ParentCtrl', function () {
  this.x.title = 'Some title';
});


app.controller('ChildCtrl', function ($scope) {
  console.log($scope.pc.x.title);
});

这很烦人,因为1)。我必须知道在html页面中我的父控制器被命名为pc。 2)。我无法进行批量搜索并替换$scope => vm (or this),因为如果继承该属性,它将无效。

有人可以告诉我controller as引入时的理由是什么?

如果我使用大量的范围继承,我是否正确,那么我应该避免使用controller as?或者范围继承被认为是有害的,应该不鼓励吗?

1 个答案:

答案 0 :(得分:3)

不,您不能进行机械搜索和替换,因为您当前将所有不同范围的值混合在一起并且控制器 - 因为语法旨在将它们分开。特别是为了避免某些父作用域使用title然后你已经离开并在子作用域中再次使用title并隐藏父作用域的情况。或者更糟糕的是,当你所做的只是在一个子范围内掩盖它时,你认为你正在更新父title

因此,您必须确定哪个父作用域包含您要访问的每个值。这意味着如果要将其从范围继承树中拉出来,您确实必须知道用于引用该范围模型的名称。

更好的解决方案是使用指令,或从1.5角向前的组件。代替子控制器爬行范围以获取父值,而不是将所需的值作为参数传递给指令/组件。然后父母负责公开他希望孩子访问的值。或者,您可以使用指令或控制器的require属性来创建仅在嵌入特定父级时工作的子级,并且父级控制器将直接绑定到子级控制器中。

以下是angular documentation的示例。请注意,在myPane控制器内部,您可以以this.tabsCtrl的形式访问父控制器,重要的是,孩子决定使用父控制器的名称,而不是父控制器:

angular.module('docsTabsExample', [])
.component('myTabs', {
  transclude: true,
  controller: function MyTabsController() {
    var panes = this.panes = [];
    this.select = function(pane) {
      angular.forEach(panes, function(pane) {
        pane.selected = false;
      });
      pane.selected = true;
    };
    this.addPane = function(pane) {
      if (panes.length === 0) {
        this.select(pane);
      }
      panes.push(pane);
    };
  },
  templateUrl: 'my-tabs.html'
})
.component('myPane', {
  transclude: true,
  require: {
    tabsCtrl: '^myTabs'
  },
  bindings: {
    title: '@'
  },
  controller: function() {
    this.$onInit = function() {
      this.tabsCtrl.addPane(this);
      console.log(this);
    };
  },
  templateUrl: 'my-pane.html'
});