【问题标题】:AngularJS multiple uses of Controller and rootScopeAngularJS对Controller和rootScope的多种使用
【发布时间】:2012-11-01 15:28:47
【问题描述】:

我想在 2 个单独的 HTML 元素上使用控制器,并在编辑一个列表时使用 $rootScope 使 2 个列表保持同步:

HTML

<ul class="nav" ng-controller="Menu">
    <li ng-repeat="item in menu">
        <a href="{{item.href}}">{{item.title}}</a>
    </li>
</ul>

<div ng-controller="Menu">
    <input type="text" id="newItem" value="" />
    <input type="submit" ng-click="addItem()" />
    <ul class="nav" ng-controller="Menu">
        <li ng-repeat="item in menu">
            <a href="{{item.href}}">{{item.title}}</a>
        </li>
    </ul>    
</div>

JS

angular.module('menuApp', ['menuServices']).
    run(function($rootScope){
        $rootScope.menu = [];
    });

angular.module('menuServices', ['ngResource']).
    factory('MenuData', function ($resource) {
        return $resource(
            '/tool/menu.cfc', 
            {
                returnFormat: 'json'
            },
            {
                getMenu: {
                    method: 'GET',
                    params: {method: 'getMenu'}
                },
                addItem: {
                    method: 'GET',
                    params: {method: 'addItem'}
                }
            }
        );
    });

function Menu($scope, MenuData) {

    // attempt to add new item
    $scope.addNewItem = function(){
        var thisItem = $('#newItem').val();

        MenuData.addItem({item: thisItem},function(data){
            $scope.updateMenu();
        });
    }   

    $scope.updateMenu = function() {
        MenuData.getMenu({},function(data){
            $scope.menu = data.MENU;
        });         
    }

    // get menu data
    $scope.updateMenu();
}

当页面加载时,ULDIV 都显示来自数据库的正确内容,但是当我使用addNewItem() 方法时,只有DIV 得到更新。

有没有更好的方法来构建我的逻辑,或者我可以做些什么来确保UL 中的$scope.menu 同时得到更新?

这是一个类似的例子:http://plnkr.co/edit/2a55gq

【问题讨论】:

  • 您可以将菜单与更新菜单本身的方法一起保存在服务中,并让服务通过 $rootScope.$broadcast 广播更新的菜单。控制器已使用 $scope.$on 监听事件并通过 $scope.menu = ... 更新对菜单的区域设置引用
  • 我更新了我的答案,以防你想知道如何在指令中做到这一点。
  • 这个答案还包括一个非常有用的视频:stackoverflow.com/a/13882619/109941

标签: javascript data-binding controller angularjs


【解决方案1】:

我建议使用包含菜单及其方法的服务。该服务将更新控制器引用的菜单。

在此处查看工作人员:http://plnkr.co/edit/Bzjruq

这是示例 JavaScript 代码:

angular
 .module( 'sampleApp', [] )
 .service( 'MenuService', [ '$rootScope', function( $rootScope ) {

   return {
      menu: [ 'item 1' ],
      add: function( item ) {
        this.menu.push( item );
      } 
   };

 }])
 .controller( 'ControllerA', [ 'MenuService', '$scope', function( MenuService, $scope ) {

   $scope.menu = MenuService.menu;

   $scope.addItem = function() {
    MenuService.add( $scope.newItem );  
   };

 }]);

以及示例 Html 页面:

<!DOCTYPE html>
<html>

  <head lang="en">
    <meta charset="utf-8">
    <title>Custom Plunker</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
    <script src="app.js"></script>
  </head>

  <body ng-app="sampleApp">

    <div ng-controller="ControllerA">
      <ul>
        <li ng-repeat="item in menu">{{item}}</li>
      </ul>
      <input type="text" ng-model="newItem" /><input type="submit" ng-click="addItem()" />
    </div>

    <div ng-controller="ControllerA">
      <ul>
        <li ng-repeat="item in menu">{{item}}</li>
      </ul>
    </div>

  </body>

</html>

【讨论】:

  • 就是这样,我只是在你发表评论后才研究这个,谢谢!
  • 不会在调用MenuSerice.add 时自动更新$scope.menu,即使没有$rootScope.$broadcast$scope.$on 的东西?由于$scope.menu直接指向MenuService.menu,所以不是它的副本。
  • @KajMagnus 谢谢,我更新了答案和相关的 Plunker。
  • 那么将同一个控制器附加到许多视图元素是一种非常糟糕的做法吗?或者在开发过程中是否有任何时候这种解决方案是可以接受的?我在 webdev 工具控制台中没有看到任何错误,当运行具有如此肮脏的 hack (?) 的 webapp 时。
【解决方案2】:

编辑:

这里是更新版本plunker。它适用于两个控制器。

主要思想是使用服务和广播将数据与指令同步。

app.service('syncSRV', function ($rootScope) {
    "use strict";
    this.sync = function (data) {
        this.syncData = data;
        $rootScope.$broadcast('updated');
    };
});
app.controller('MainCtrl1', ['$scope', function ($scope) {
    }])
    .controller('MainCtrl2', ['$scope', function ($scope) {
    }]);
app.directive('sync',function (syncSRV) {
    "use strict";
    return {
        template: '<div><input ng-model="syncdata" type="text" /></div> ',
        controller: function ($scope, $element, $attrs) {
            $scope.$watch('syncdata', function (newVal, oldVal, $scope) {
                syncSRV.sync(newVal);
            }, true);
        }
    };
}).directive('dataview', function (syncSRV) {
    "use strict";
    return {
        template: '<div>Sync data : {{data}}</div> ',
        controller: function ($scope, $element, $attrs) {
            $scope.$on('updated', function () {
                $scope.data = syncSRV.syncData;
            });
        }
    };
});


<div ng-controller="MainCtrl1">
    <fieldset>
        <legend> Controller 1</legend>
        <div dataview></div>
        <div sync></div>
    </fieldset>
</div>
<div ng-controller="MainCtrl2">
    <fieldset>
        <legend> Controller 2</legend>
        <div dataview></div>
        <div sync></div>
    </fieldset>
</div>

这就是我会为这种情况做的事情。

我将为

创建一个指令
<ul class="nav" ng-controller="Menu">
        <li ng-repeat="item in menu">
            <a href="{{item.href}}">{{item.title}}</a>
        </li>
</ul> 

所以一旦项目更新,它将在两个指令中更新。

small example

【讨论】:

  • 您的示例只有 1 个控制器,这仍然可以跨 2 个不同的控制器工作吗?例如:plnkr.co/edit/2a55gq。我在如何修改我的页面 HTML 方面受到限制
【解决方案3】:

我只想更新和简化所选答案。看来您可以通过删除此行来减少这种情况: $rootScope.$broadcast( 'MenuService.update', this.menu );

还有这个块:

$scope.$on( 'MenuService.update', function( event, menu ) {
 $scope.menu = menu;
});

原因是,我们已经在使用 Service,它基本上绑定了两个相同的控制器,因此无需使用 $rootScope.$broadcast 并添加 observable。

在这里工作: http://plnkr.co/edit/1efEwU?p=preview

您只需要链接服务,当我重构代码时,我能够将其减少到 13 行而不是 22 行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-21
    • 2013-11-03
    • 1970-01-01
    • 2016-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多