ui router - 具有共享控制器的嵌套视图

时间:2015-01-04 17:04:09

标签: angularjs angular-ui-router

我有一个抽象的父视图,用于与控制器的嵌套视图共享。

.state('edit', {
    abstract: true,
    url: '/home/edit/:id',
    templateUrl: 'app/templates/editView.html',
    controller: 'editController'
})
.state('edit.details', {
    url: '/details',
    templateUrl: 'app/templates/editDetailsView.html'
})
.state('edit.info', {
    url: '/info',
    templateUrl: 'app/templates/editInfoView.html'
})

路由按预期工作。

问题在于,当我从其中一个嵌套视图更新$scope变量时,更改不会反映在视图中。当我从父视图中执行相同操作时,它可以正常工作。这不是需要$apply的情况。

我的猜测是为每个视图创建了一个editController的新实例,但我不确定为什么或如何修复它。

4 个答案:

答案 0 :(得分:41)

这里的问题与此Q&答:How do I share $scope data between states in angularjs ui-router?

如何解决它的方式隐藏在:

Understanding Scopes

  

在AngularJS中,子范围通常原型继承自其父范围    ...

     

有一个'。'在你的模型中将确保原型继承发挥作用。

// So, use
<input type="text" ng-model="someObj.prop1"> 
// rather than
<input type="text" ng-model="prop1">.

还有这个

Scope Inheritance by View Hierarchy Only

  

请记住,只有嵌套状态的视图时,范围属性才会继承状态链。范围属性的继承与状态的嵌套无关,也与视图嵌套(模板)无关。

     

完全有可能您有嵌套状态,其模板在您网站中的各种非嵌套位置填充ui-views。在这种情况下,您不能期望在子状态视图中访问父状态视图的范围变量。

我们应该在编辑Controller

中执行此操作
controller('editController', function ($scope) {
  $scope.Model = $scope.Model || {SomeProperty : "xxx"};
})

我们甚至可以重用controller: 'editController' (我们不必这样做,因为$ scope.Model会在那里 - 感谢继承)

.state('edit', {
    abstract: true,
    url: '/home/edit/:id',
    templateUrl: 'app/templates/editView.html',
    controller: 'editController'
})
.state('edit.details', {
    url: '/details',
    templateUrl: 'app/templates/editDetailsView.html',
    controller: 'editController'
})
.state('edit.info', {
    url: '/info',
    templateUrl: 'app/templates/editInfoView.html',
    controller: 'editController'
})

现在,同一个控制器将被多次实例化(父所有孩子),但$scope.Model只会启动一次(在父级内部)并且随处可用

选中此similar working example here

答案 1 :(得分:14)

基于PilotBob

的评论
  

当使用 controllerAs 模式给孩子说明自己的控制器时,是否可以这样做?

我决定使用controllerAs追加另一个解决方案,同时保留above/original concept

a working plunker

状态现在将具有不同的控制器,父状态将其命名为“parentCtrl” (不会在具有子控制器的子范围中覆盖)

 .state("main", {
      controller:'mainController',
      controllerAs: "parentCtrl",
      ...
  .state("main.1", {
      parent: 'main',
      controller:'child1Controller',
      controllerAs: "ctrl",
      ...
  .state("main.2", {
      parent: 'main',
      controller:'child2Controller',
      controllerAs: "ctrl", 
      ... 

这些是控制者:

.controller('mainController', function ($scope) {
    this.Model =  {Name : "yyy"};
})
.controller('child1Controller', function ($scope) {
    $scope.Model = $scope.parentCtrl.Model;
})
.controller('child2Controller', function ($scope) {
    $scope.Model = $scope.parentCtrl.Model; 
})

检查行动here

答案 2 :(得分:1)

使用resolve

的另一种选择
.state('edit', {
    abstract: true,
    url: '/home/edit/:id',
    templateUrl: 'app/templates/editView.html',
    controller: 'editController',
    resolve: {
        baseData: function() {
            return {};
        }
    }
})
.state('edit.details', {
    url: '/details',
    templateUrl: 'app/templates/editDetailsView.html',
    controller: 'editController'
})
.state('edit.info', {
    url: '/info',
    templateUrl: 'app/templates/editInfoView.html',
    controller: 'editController'
})


.controller('editController', function (baseData) {
    baseData.foo = baseData.foo || 'bar';
});

答案 3 :(得分:0)

在子控制器中,你可以这样做:

angular.extend($scope, $scope.$parent)

如果控制器与别名一起使用,例如'vm'你能做到:

let vm = angular.extend(this, $scope.$parent.vm);