角度控制器范围继承与服务

时间:2015-12-22 18:19:54

标签: angularjs angularjs-scope angular-services

在我的网站上,我有一个导航栏组件,我想为每个最终加载的ng-view自定义。目前,我这样做如下。我有一个导航栏本身的NavCtrl,我的ng-view指令位于该控制器的范围之外。我使用导航栏服务来更改/覆盖导航栏中的功能,例如,我的每个视图都需要覆盖导航栏的保存按钮的单击处理程序。 NavbarService设置了保存功能。在NavCtrl中,$ scope.save = NavbarService.save



var app = angular.module('myApp', ['ngRoute', 'ngResource']);

app.config(function($routeProvider) {
  $routeProvider.when('/world', {templateUrl : 'world.html', controller : 'WorldCtrl'}).otherwise({templateUrl : 'hello.html', controller : 'HelloCtrl'});
});

app.service('NavbarService', function() {
  var message = 'Save button not clicked yet',
      saveFunction = function() {
    this.setMessage('Default save called');
  };
  
  this.save = function() {
    _saveFunction();
  };
  
  this.setSaveFunction = function(funct) {
    _saveFunction = funct;
  };
  
  this.setMessage = function(newMessage) {
    message = newMessage;
  };
  
  this.getMessage = function() {
    return message;
  }
});

app.controller('NavCtrl', function($scope, $location, NavbarService) {
  $scope.message = NavbarService.getMessage();
  $scope.save = NavbarService.save;
  $scope.world = function() {
    $location.path('/world');
  };
  
  $scope.hello = function() {
    $location.path('/hello');
  };
  
  $scope.$watch(NavbarService.getMessage, function(newValue) {
    $scope.message = newValue;
  });
});

app.controller('HelloCtrl', function($scope, NavbarService) {
  $scope.init = function() {
    NavbarService.setSaveFunction(function() {
      NavbarService.setMessage('Save method called from the HelloCtrl');
    });
  };
});

app.controller('WorldCtrl', function($scope, NavbarService) {
  $scope.init = function() {
    NavbarService.setSaveFunction(function() {
      NavbarService.setMessage('Save method called from the WorldCtrl');
    });
  };
});

<html lang="en">

<head>
  <title>My App</title>
</head>

<body ng-app="myApp">
  <nav ng-controller="NavCtrl">
    <button ng-click="save()">Save</button>
    <button ng-click="world()">Go to world</button>
    <button ng-click="hello()">Go to hello</button>
    <pre>{{message}}</pre>
  </nav>

  <div ng-view onload="init()"></div>

  <script type="text/ng-template" id="hello.html">
    <h2>Active view is Hello</h2>
  </script>

  <script type="text/ng-template" id="world.html">
    <h2>Active view is World</h2>
  </script>

  <script src="https://code.jquery.com/jquery-2.1.4.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular-route.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular-resource.js"></script>
</body>

</html>
&#13;
&#13;
&#13;

我想知道我是否太复杂了。我认为只需将ng-view指令嵌套在NavCtrl的范围内即可实现同样的目的。这样我就可以在每个视图控制器中覆盖$ scope.save。

但大多数文档都声明服务是跨控制器共享资源的首选方式。有一种方式比另一种更好吗?为什么呢?

请指教。感谢。

2 个答案:

答案 0 :(得分:0)

序言&amp;一些历史

让我提出既不利用服务也不利用范围继承的东西。这是我试图解决的类似问题。我有通过 ui.router的 ui-view加载的视图(我们基本上在这里谈论应用程序状态吗?)。我有我的正常应用程序nav&amp; subnav,像这样...

['findings', 'tasks', 'reports', 'blah']

......所有观点都很常见。但是我也有一些特定的UI视图,我希望连接到该视图的$ scope / controller。换句话说,我可能有一个特定于当前视图的下拉列表,以及另一个视图的预先输入。我选择用声明来解决这个问题,而不是试图强制一些继承覆盖范式。

指令

nxViewMenuContainer

首先,我制作了一个指令,它只是一个容器,用于容纳特定于视图的nav元素的声明元素。

.directive 'nxViewMenuContainer', [
    ()->
        dir =
            restrict: 'EAC'
            link: ($scope, elem, attrs)->
                if !elem.attr 'id'
                    elem.attr 'id', 'nx-view-menu-container'

                $scope.$root.$on '$stateChangeSuccess', ->
                    elem.empty()
]

这是这样实现的:

<div class="navbar navbar-submenu2">
        <ul class="nav navbar-nav navbar-submenu2">

            <!-- container for dynamic view-specific menu content -->
            <li nx-view-menu-container class="nav navbar-nav">
            </li>

            <!-- static nav items -->
            <li class="nav navbar-nav" ng-repeat="nav in app.subnav">
                <a><span class="current-del-name">{{nav.label}}</span></a>
            </li>

nxViewMenu

此指令在每个视图上声明,并作为特定于视图的元素容器移动到上面的指令。

.directive 'nxViewMenu', [
    ()->
        dir =
            restrict: 'EAC'
            link: ($scope, elem, attrs)->

                elem.ready ->
                    name = attrs.nxViewMenu or '#nx-view-menu-container'
                    target = $(name)

                    if !target.length
                        return

                    target.empty()
                    target.append elem

]

在每个视图中,我可能希望动态菜单出现在其他位置(在本例中,在app-level的导航容器中),我在视图模板中声明它。

查看1

<div class="nx-view-menu">
    <a class="btn btn-default">
        <i class="fa fa-lg fa-bars nx-clickout-filter"></i>
        <!--<label name="panel-title" style="color:#58585b; padding-left:5px;" class="nx-clickout-filter">Quick Links</label>-->
    </a>
</div>                

查看2

<div class="nx-view-menu">
    <input typeahead="...">
</div>

查看3

<div class="nx-view-menu">
    <date-picker>...</date-picker>
</div>

结束参数

  • 首先,你是以声明方式做事,所以一旦理解了这些指令,就很容易遵循逻辑。
  • 它使顶层控制器不知道特定视图的逻辑,同时仍以一致的方式显示DOM
  • 它绕过了对特殊服务或一些hacky消息总线的需求
  • 它简化了整体DOM结构,因为您不需要设置一些双视图容器(在我的情况下,这是使用 ui.router 的另一个选项)或并行状态。 / LI>

答案 1 :(得分:0)

  

但是,如果因为我的所有观看都需要该按钮,我将不得不在每个模板中重复<button ng-click="save()">Save</button>元素。

此评论清除了一些内容,在这种情况下,我不一定会推荐dynamic/declarative方法。相反,这个特定的用户体验可以被视为更多的静态用户体验,我做了一些不同的事情,仍然避免任何服务范围继承。我建议使用以下内容:

app level

您的应用级导航仍然可以借用其他答案的建议,但请忽略动态/声明性内容。导航看起来像:

<div class="navbar navbar-submenu2">
    <ul class="nav navbar-nav navbar-submenu2">

        <!-- container for dynamic view-specific menu content -->
        <li nx-view-menu-container class="nav navbar-nav">
        </li>

        <!-- static nav items -->
        <li class="nav navbar-nav">
            <a ng-click="saveRqst()">
                <span class="current-del-name">Save</span>
            </a>
        </li>

应用级控制器会有:

.controller 'appController', [
    '$scope'
    ($scope)->
         $scope.saveRqst = ->
             $scope.$root.$broadcast 'saveRqst', <addt. data if applicable>
]
每个视图级别控制器

这做了一些假设:

  • 仅存在这些类型的视图(意味着它是每个应用程序状态的一个视图/控制器,否则您可能有2个控制器尝试处理保存rqst)
  • 您的观点作为孩子存在于您的应用模板中
  • 您的观点遵循普通的视图控制器范例(意味着它们不是具有隔离范围的特殊指令)

    .controller&#39; someController&#39;,[      &#39; $范围&#39;      ($范围) - &GT;          $ scope。$ on&#39; saveRqst`,(data) - &gt; #do这个观点的东西 ]

现在我知道这看起来有点像范围继承位,但事实并非如此。无论如何,你必须为每个视图控制器逻辑定义你的保存逻辑,如果我已经理解你在问题中提出的内容以及其他答案的评论,那就无法解决。关于这个事件总线方法的一点好处就是它很容易跟踪逻辑。如果您记下您的代码,那么另一位有使用Java或Flex或其他任何经验的开发人员将能够轻松地快速实现其他视图。保存逻辑。

所以我提出了两种方法。一个是more dynamic, declarative approach,这个是更静态但又独一无二的方法。

相关问题