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



在我的网站上,我有一个导航栏组件,我想为我最终加载的每个ng视图自定义它。目前,我正在做以下工作。我有一个导航条本身的NavCtrl,我的ng视图指令位于这个控制器的范围之外。我使用导航栏服务来更改/覆盖导航栏中的功能,例如,我的每个视图都需要覆盖导航栏上保存按钮的点击处理程序。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>

我想知道我是不是把这件事搞得太复杂了。我认为同样的事情可以通过在NavCtrl的范围内嵌套ng视图指令来实现。这样,我就可以在每个视图控制器中覆盖$scope.save。

但大多数文档都指出,服务是跨控制器共享资源的首选方式。一种方法比另一种好吗?为什么?

请告知。谢谢

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

这条评论澄清了一些问题,在这种情况下,我不一定推荐动态/声明性方法。相反,这个特定的用户体验可以被认为更像是静态用户体验,我会做一些不同的事情,仍然避免任何服务范围继承。我建议使用以下方法:

应用程序级别

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

<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>
]

按视图级别控制器

这做了一些假设:

  • 这些类型的视图中只有一个存在(这意味着每个应用程序状态只有一个视图/控制器,否则可能有两个控制器试图处理保存rqst)
  • 您的视图作为子视图驻留在应用程序模板中
  • 您的视图遵循正常的视图控制器范式(意味着它们不是具有隔离作用域的特殊指令)

    .controller"someController"['$scope'($scope)->$scope$在"saveRqst"上,(数据)->#为此视图做些什么]

现在我知道这看起来有点像作用域继承,但事实并非如此。无论如何,您必须定义每个视图的保存逻辑控制器逻辑,如果我已经理解您在问题和其他答案的评论中所做的内容,就无法绕过这一点。这种事件总线方法的一个优点是它很容易跟踪逻辑。如果你记下你的代码,那么另一个有过Java或Flex或其他方面工作经验的开发人员将能够很容易地跟上实现额外视图的速度。保存逻辑。

因此,我提出了两种方法。一种是更动态的、声明性的方法,而另一种则是更静态的、但每视图唯一的方法。

序言&一些历史

让我提出一个既不使用服务,也不使用作用域继承的方案。这是我试图解决的一个类似问题。我有一些视图是通过ui.router的ui-view加载的(我们基本上是在这里讨论应用程序状态,对吧?)。我有我的正常应用程序nav&亚纳夫,类似这样的。。。

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

这是所有观点的共同点。但我也有一些特定的UI每个视图,我希望有线到该视图的$scope/控制器。换言之,我可能有一个特定于当前视图的下拉列表,以及另一个视图的预编。我选择用一个指令来声明性地解决这个问题,而不是试图强制一些继承重写范式。

指令

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>

nxView菜单

该指令在每个视图上声明,并用作移动到上述指令的视图特定元素容器。

.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
]

在我可能希望动态菜单出现的每个视图中(在本例中,在应用程序级别的导航容器中),我在视图模板中声明它。

视图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
  • 它绕过了对特殊服务或一些黑客信息总线的需求
  • 它简化了您的整体DOM结构,因为您不需要设置一些双视图容器(在我的案例中,这是使用ui.router的另一个选项)或并行状态

最新更新