【问题标题】:Two views in one AngularUI Router state sharing scope一个 Angular UI Router 状态共享范围中的两个视图
【发布时间】:2014-03-21 04:21:35
【问题描述】:

我对 AngularUI 路由器非常陌生,希望将其用于以下场景:

所有页面通用的布局包括顶部导航栏,其中包含右侧带有按钮的菜单和填充下方空间的内容部分。该页面有几个页面,我映射到 UI 路由器状态(page1、page2、...)。每个页面都可以有自己的菜单项和自己的内容。 菜单需要与内容共享范围,因为它们会交互(例如,保存按钮在内容中提交表单,只有在表单有效时才应启用)。

HTML 大致如下:

<body>
    <nav class="...">
        <h1>my site</h1>
        <div>MENU SHOULD GO HERE</div>
    </nav>
    <div class="row">
        <div class="column ...">
            CONTENT SHOULD GO HERE
        </div>
    </div>        
</body>

现在,我为每个状态使用两个并行视图和两个控制器。但是这样一来,两个作用域/控制器就不能交互了。

那么你将如何实现呢?

【问题讨论】:

    标签: angularjs angular-ui angular-ui-router


    【解决方案1】:

    $scope 不是模型,它是对模型的引用,粘在数据和视图之间。如果 $scope 有两个或更多,控制器需要共享一个模型/状态/数据,通过注册一个角度服务来使用单例对象实例。可以将一个服务/工厂注入到任意数量的控制器中,然后一切都可以从这一事实来源中发挥作用。

    这是 1 个工厂将导航栏和正文中的 $scopes 与 ui-router http://plnkr.co/edit/P2UudS?p=preview 链接的演示(仅限左侧选项卡)

    ui-router(viewC 是导航栏):

    $stateProvider
    .state('left', {
      url: "/",
      views: {
        "viewA": {
          controller: 'LeftTabACtrl',
          template: 'Left Tab, index.viewA <br>' +
                    '<input type="checkbox" ng-model="selected2.data" />' +
                    '<pre>selected2.data: {{selected2.data}}</pre>'
        },
        {...},
        "viewC": {
          controller: 'NavbarCtrl',
          template: '<span>Left Tab, index.viewC <div ui-view="viewC.list"></div>' +
                    '<input type="checkbox" ng-model="selected.data" />' +
                    '<pre>selected.data: {{selected.data}}</pre></span>'
        }
      }
    })
    

    工厂和控制器:

    app.factory('uiFieldState', function () {
        return {uiObject: {data: null}}
    });
    
    app.controller('NavbarCtrl', ['$scope', 'uiFieldState', '$stateParams', '$state',
        function($scope, uiFieldState, $stateParams, $state) {
            $scope.selected = uiFieldState.uiObject;
        }
    ]);
    
    app.controller('LeftTabACtrl', ['$scope', 'uiFieldState', '$stateParams', '$state',
        function($scope, uiFieldState, $stateParams, $state) {
            $scope.selected2 = uiFieldState.uiObject;
        }
    ]);
    

    如您所见,工厂对象{uiObject: {data: null}} 被注入到带有uiFieldState 的控制器中,然后它只是$scope.selected = uiFieldState.uiObject; 用于将工厂连接到范围ng-model="selected.data" .`

    【讨论】:

    • 我最终做到了。我意识到我通常缺乏关于如何连接 AngularJS 应用程序部分的知识(所以这个问题实际上与 UI 路由器无关)。
    • 我只想要data,所以我尝试设置app.factory('uiFieldState', function () { return {data: null}})$scope.data=uiFieldState.data。当然,这不起作用,因为需要一个对象在这样的范围之间传递数据。
    【解决方案2】:

    你应该使用:

    $on and $emit
    

    发送数据的发射控制器。

    angular.module('MyApp').controller('MyController', ['$scope', '$rootScope', function ($scope, $rootScope){
    
      $rootScope.$emit('SomeEvent', data);
    }]);
    

    如何以安全的方式实现 $rootScope 以便在使用后销毁和修复内容的示例:

    angular
    .module('MyApp')
    .config(['$provide', function($provide){
        $provide.decorator('$rootScope', ['$delegate', function($delegate){
    
            Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
                value: function(name, listener){
                    var unsubscribe = $delegate.$on(name, listener);
                    this.$on('$destroy', unsubscribe);
                },
                enumerable: false
            });
    
    
            return $delegate;
        }]);
    }]);
    

    还有应该处理数据的控制器。

    angular.module('MyApp')
    .controller('MySecondController', ['$scope', function MyController($scope) {
    
            $scope.$onRootScope('SomeEvent', function(event, data){
                console.log(data);
            });
        }
    ]);
    

    您可以传入 $rootScope 而不是使用我们在配置中定义的 $scopes 方法 $onRootScope。但是,这不是使用 $emit 和 $onRootScope 的推荐方式。

    你总是可以使用 $broadcast,而不是使用 $emit。然而,随着您的应用程序的增长,这会给您带来非常巨大的性能问题。因为它通过所有控制器冒泡数据。

    如果您的控制器是彼此的父级和子级,则它们不必使用 $rootScope。

    这里是 $emit 和 $broadcast 之间区别的示例:jsFiddle

    它们的性能差异确实很大:

    【讨论】:

    • 感谢您的全面回复!是否有另一种使用 UI 路由器的方法,以便我最终获得两个视图的相同范围?这比在根范围上使用事件更方便...
    • 如果您将范围作为数据变量发送,如图所示,数据将绑定回另一个。例如,在索引文件中,您可以在 ng-click 指令中有一个名为“sendUserData({{userData}}”的方法。在控制器中,您有 $scope.sendUserData = function(data){$rootScope.$ emit('LoadUser', data);} 从第二个控制器中,您只需将数据绑定到新的当前范围: $scope.$onRootScope('LoadUser', function(event, data){$scope.user = data;} 数据将被绑定。
    • 如果您认为它对您有帮助,请考虑将其标记为答案。
    • 我意识到我缺乏关于如何连接 AngularJS 应用程序部分的知识。您的回答很有趣,让我对事件以及发射和广播之间的区别有了一些了解。谢谢。另一个答案更好地解决了我的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多