AngularJS:ng-include和ng-controller

27
我正在使用angular构建一个应用程序,需要构建8-10个视图。 所有视图都有一个共享底部,在基于视图和一组业务规则的情况下,我需要有条件地显示/隐藏底部的某些内容。
因此, 我为每个视图单独编写了控制器,并编写了一个底部控制器。 我使用ng-include包含共同的底部布局,其中包含的html引用底部控制器在ng-controller中。
Index.html
<body ng-controller="MainCtrl as vm">
    <p>Message from Main Controller '{{vm.mainMessage}}'</p>
    <div ng-include="'commonFooter.html'"></div>
</body>

commonFooter.html

<div ng-controller="FooterCtrl as vm">
    <p>Message from Footer Controller '{{vm.message}}'</p>
    <p ng-show="vm.showSomthing">Conditional footer Content</p>
</div>

我希望每个视图控制器都能确定页脚的状态以及特定内容是否隐藏。 (shouldDisplaySomthingInFooter 如下)
app.controller('MainCtrl', function($scope) {
  var vm = this;
  vm.mainMessage= 'HEELO';
  vm.shouldDisplaySomthingInFooter = true;
  window.console.log('Main scope id: ' + $scope.$id);
});

我原本打算在FooterController中访问父控制器,并提取特定设置以基于业务规则启用/禁用内容。

app.controller('FooterCtrl', function($scope) {
    var vm = this;
  vm.message = 'vm footer';

  window.console.log('Footer scope id: ' + $scope.$id);
  window.console.log('Footer parent scope id: ' + $scope.$parent.$id);
  window.console.log('Footer grandparent scope id: ' + $scope.$parent.$parent.$id);
  window.console.log('Footer grandparent scope name: ' + $scope.$parent.$parent.mainMessage);
  window.console.log('Footer grandparent scope condition: ' + $scope.$parent.$parent.shouldDisplaySomthingInFooter);

  vm.showSomthing = false; //how to pull from parent scope to bind the ng-show to a value set in the parent from within a ng-include?
});

我这里有一个例子: http://plnkr.co/edit/ucI5Cu4jjPgZNUitY2w0?p=preview

我的问题是,当我进入父级作用域以提取内容时,它返回未定义,我不知道为什么。

通过检查scopeid,我可以看到作用域嵌套到祖父级别,我认为这是因为ng-include在视图作用域下添加了一个额外的作用域层。 附加示例中控制台输出

额外提示:如果我不使用 $scope 对象,而是坚持使用 var vm = this; 的方式来完成它,那将更可取。但乞丐没有选择 :)

app.controller('MainCtrl', function($scope) {
  var vm = this;

非常感谢你提前的帮助。
3个回答

32
如果您将外部控制器作用域定义为vm,将内部控制器定义为foo,则可以轻松地将它们分开并在内部控制器中引用vm
演示
<body ng-controller="MainCtrl as vm">
    <p>Message from Main Controller '{{vm.mainMessage}}'</p>
    <div ng-include="'commonFooter.html'"></div>
</body>

CommonFooter.html:

<div ng-controller="FooterCtrl as footer">
    <p>Message from Footer Controller '{{footer.message}}'</p>
    <p ng-show="vm.shouldDisplaySomethingInFooter">Conditional footer Content</p>
</div>

app.js:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function() {
    var self = this;
    self.mainMessage = 'Hello world';
    self.shouldDisplaySomethingInFooter = true;
});

app.controller('FooterCtrl', function() {
    var self = this;
    self.message = 'vm footer';
});

注意:我将你的var vm = this重命名为var self = this,以提高清晰度,并减少视图和控制器之间的混淆。

期望输出:

显示/隐藏项目的条件性输出


3
你误解了controller as语法(请参阅文档)的用途。它只是一种在本地作用域中公开特定控制器以便您可以从模板访问其属性的方法。当您在父级和页脚模板中使用someController as vm时,您并没有在控制器之间创建任何连接。您只是在页脚的作用域上设置了一个vm属性,因此当您在页脚模板中使用它时,您正在访问页脚的控制器(并且您已经阻止了访问父控制器的方式)。
对于您尝试做的事情,您基本上根本不需要controller as语法。只需将数据正确放置在$scope上,让作用域层次结构为您完成剩下的工作即可。
在您的父控制器中:
$scope.features.rock = true;
$scope.features.roll = false;

在您的页脚模板中
<p ng-show="features.rock">...</p>
<p ng-show="features.roll">...</p>

现在,您还可以从其他控制器中查看和更改功能(因为它们的范围是父控制器范围的后代)。

2

我玩弄了你的plunker,但也将var vm = this;更改为$scope,所以我在额外的部分失败了 :-)

我强烈反对使用$scope.$parent,正如你展示的那样。各种指令,如ng-includeng-show等,都会生成自己的作用域。

如果将来有人更改您的html并添加作用域,无法控制。

建议使用驻留在MainCtrl上的函数,并通过继承作用域访问它们。

Plunker

$scope.getShouldShow = function() {
    return $scope.shouldDisplaySomthingInFooter;
  };
  $scope.setShouldShow = function(val) {
    $scope.shouldDisplaySomthingInFooter = val;
  };

  $scope.getMainMessage = function () {
    return $scope.mainMessage;
  }

并称之为:

<p ng-show="getShouldShow();">Conditional footer Content</p>

并且:

  window.console.log('Footer grandparent scope name: ' + $scope.getMainMessage());
  window.console.log('Footer grandparent scope condition: ' + $scope.getShouldShow());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接