6

我有一个角度控制器,可以在创建时获取资源:

angular.module('adminApp')
  .controller('PropertiesCtrl', function ($log, $scope, Property, $location) {
    $scope.properties = Property.query()  
  });

现在我想向控制器添加逻辑,以便能够创建“属性”资源:

angular.module('adminApp')
  .controller('PropertiesCtrl', function ($log, $scope, Property, $location) {
    $scope.properties = Property.query()  
    $scope.create = function(){
      //logic to create
    };
  });

但是,当我在表单上创建“属性”时,会进行不必要的调用以首先获取所有属性。我该如何避免这种情况?


潜在的解决方案?

  1. 我可以创建一个单独的控制器,专门用于创建不会获取属性的属性。但是,将单个资源的所有 CRUD 操作封装在单个控制器下会变得更简单。
  2. 我可以创建一个函数来获取所有属性。但是,我的索引页面直接使用“属性”。我首先需要调用一些方法来获取数据,然后使用数据(不知何故?)
4

3 回答 3

17

我的反应是,听起来您正在尝试将控制器用作服务,并且您正在尝试将许多功能放入一个控制器中。

因此,您应该考虑两件主要的事情。首先,创建每个只有一个特定目的的控制器是相当重要的。请注意,这与使用一次控制器不同,如果您有一个应该出现在多个地方的功能,我们鼓励您在多个不同的地方使用同一个控制器。这只是意味着控制器不应该同时做几件事。

让我们以照片库为例。虽然您可以创建一个控制器来获取所有照片,让您添加新照片,并让您编辑和删除现有照片,但这不是一个好主意。如果您决定也可以从另一个页面“Page X”添加照片怎么办?如果您在哪里重用相同的控制器,那么您还将从服务器请求图库并为您不打算在该页面上出现的内容设置控件。

如果您改为制作一个仅负责获取内容的控制器、一个用于添加新照片的单独控制器、另一个用于编辑等,那么这将很容易。您只需在“Page X”上实现创建控制器,您不必担心意外触发超出您的预期。您可以选择在页面上完全实现您想要的功能,并且实现该功能。这也使您的控制器体积小、易于阅读和快速编辑/错误修复,所以这是双赢的!

其次,如果您想将所有 CRUD 资源收集到一个对象中(我也想做),它们不应该在控制器中,它们应该在service中。所以你有一个 PhotoAPI,它公开了 CREATE、READ、UPDATE 和 DELETE 函数。然后您的索引控制器只调用 READ 函数,您的创建控制器调用 CREATE 函数等。控制器定义了哪些函数和数据在哪里可用,但逻辑​​在组合服务中。这样,您可以集中资源以使它们易于查找,而不会产生组合控制器的问题。

所以像:

app.service('PhotoAPIService', [
function() {
   this.READ = function() {
     // Read logic
   }

  this.CREATE = function() {
     // Create logic
   }
}]);

app.controller('PhotoIndexController', [
'$scope',
'PhotoAPIService',
function($scope, PhotoAPIService) {
   $scope.photos = PhotoAPIService.READ(<data>);
}]);


app.controller('PhotoCreateController', [
'$scope',
'PhotoAPIService',
function($scope, PhotoAPIService) {
   $scope.createPhoto = PhotoAPIService.CREATE;
}]);
于 2013-08-26T09:55:30.327 回答
7

我从您的问题(以及您的 SO 标签)中看到您想在 AngularJS 中创建一个类似 Rails 的控制器。由于两个框架(Rails 和 AngularJS)共享相似的 MVC 原理,这实际上很容易实现。

这两个框架都允许您指示不同的路由使用相同的控制器。

在 Rails 中,您常用的 index/show/new/edit/destroy 方法(操作)是开箱即用的(带有脚手架)。这些默认操作映射到不同的、完善的路由和 HTTP 方法。

Rails 中的 CRUD/列出路线 在此处输入图像描述

现在,在 AngularJS 应用程序(或所有 SPA)中,您只需要这些路由的一个子集,因为客户端路由只理解 GET 请求:

AngularJS 中的 CRU/列表路由 在此处输入图像描述

AngularJS 本身不提供会为您生成所有 CRUD 路由的脚手架机制。但尽管如此,它为您提供了至少两种不同的方式来使用单个控制器连接您的 CRUD/List 路由。

选项 1(使用$location.path()

使用 location.path() 方法,您可以PhotosCtrl根据位置路径构建不同的操作。

路线:

app.config(
  [
    '$routeProvider',
    function ($routeProvider) {

      $routeProvider
        .when('/photos', {
          templateUrl: 'photos/index.html',
          controller: 'PhotosCtrl'
        })
        .when('/photos/new', {
          templateUrl: 'photos/new.html',
          controller: 'PhotosCtrl'
        })
        .when('/photos/:id', {
          templateUrl: 'photos/show.html',
          controller: 'PhotosCtrl'
        })
        .when('/photos/:id/edit', {
          templateUrl: 'photos/edit.html',
          controller: 'PhotosCtrl'
        });

    }
  ]
);

控制器:

app.controller('PhotosCtrl', [
  '$scope',
  'Photos', // --> Photos $resource with custom '$remove' instance method
  '$location',
  '$routeParams',
  function($scope, Photos, $location, $routeParams){
    if($location.path() === '/photos'){
      // logic for listing photos
      $scope.photos = Photos.query();
    }

    if($location.path() === '/photos/new'){
      // logic for creating a new photo
      $scope.photo = new Photos();
    }

    if(/\/photos\/\d*/.test($location.path())){ // e.g. /photos/44
      // logic for displaying a specific photo
      $scope.photo = Photos.get({id: $routeParams.id});
    }

    if(/\/photos\/\d*\/edit/.test($location.path())){ // e.g. /photos/44/edit
      // logic for editing a specific photo
      $scope.photo = Photos.get({id: $routeParams.id});
    }

    // Method shared between 'show' and 'edit' actions
    $scope.remove = function(){
      $scope.photo.$remove();
    }

    // Method shared between 'new' and 'edit' actions
    $scope.save = function(){
      $scope.photo.$save();
    }

  }
]);

这四个ifs让控制器看起来有点乱,但是当用一个替换 4 个不同的控制器时,很少有条件是不可避免的。

选项 2(使用解析属性)

此选项resolve使用路由配置对象的属性为不同的路由生成不同的“动作标识符”。

路线:

app.config(
  [
    '$routeProvider',
    function ($routeProvider) {

      $routeProvider
        .when('/photos', {
          templateUrl: 'photos/index.html',
          controller: 'PhotosCtrl',
          resolve: {
            action: function(){return 'list';}
          }
        })
        .when('/photos/new', {
          templateUrl: 'photos/new.html',
          controller: 'PhotosCtrl',
          resolve: {
            action: function(){return 'new';}
          }
        })
        .when('/photos/:id', {
          templateUrl: 'photos/show.html',
          controller: 'PhotosCtrl',
          resolve: {
            action: function(){return 'show';}
          }
        })
        .when('/photos/:id/edit', {
          templateUrl: 'photos/edit.html',
          controller: 'PhotosCtrl',
          resolve: {
            action: function(){return 'edit';}
          }
        });

    }
  ]
);

控制器:

app.controller('PhotosCtrl', [
  '$scope',
  'Photos',
  '$routeParams',
  'action'
  function($scope, Photos, $routeParams, action){
    if(action === 'list'){
      // logic for listing photos
      $scope.photos = Photos.query();
    }

    if(action === 'new'){
      // logic for creating a new photo
      $scope.photo = new Photos();
    }

    if(action === 'show')
      // logic fordisplaying a specfiic photo
      $scope.photo = Photos.get({id: $routeParams.id});
    }

    if(action === 'edit')
      // logic for editing a specfic photo
      $scope.photo = Photos.get({id: $routeParams.id});
    }

    // Method shared between 'show' and 'edit' actions
    $scope.remove = function(){
      $scope.photo.$remove();
    }

    // Method shared between 'new' and 'edit' actions
    $scope.save = function(){
      $scope.photo.$save();
    }

  }
]);

这两种方法都需要在控制器中使用一些条件,但第二种方法至少更清晰易读,因为确切的操作在路由机制内部解决,这会从繁忙的控制器中移除一些逻辑。

当然,在任何实际应用程序中,您可能会在控制器内部定义更多方法,在这种情况下,您的控制器可能会变得非常不可读。这些示例使用一个简单的 $resource 实例 ( Phones),它依赖于一个简单的 RESTfull 后端 API(Rails?)。但是,当您的视图逻辑变得复杂时,您可能需要使用 Angular 服务/工厂来抽象控制器中的一些代码。

于 2013-08-25T21:58:21.577 回答
0

使用同一资源拥有多个视图(和控制器)很好......这不是一个糟糕的设计。

如果您需要超过 1 个资源来执行所有 CRUD 操作,那将是一个问题。

使用您的第一个解决方案。每个视图 1 个控制器。它是重新组合所有 CRUD 操作的资源,而不是单个控制器。

于 2013-08-25T19:38:22.057 回答