为所有CRUD操作使用相同的控制器(Rails相似)

Kar*_*ran 6 crud angularjs angularjs-ng-repeat

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

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

现在我想向控制器添加逻辑以便能够创建"Property"资源:

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

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


潜在解决方案

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

Eri*_*onn 16

我的反应是,听起来你正在尝试将控制器用作服务,就像你试图将许多功能集成到一个控制器中一样.

所以你应该考虑两件主要的事情.首先,创建控制器非常重要,每个控制器只有一个特定的用途.请注意,这是一样的东西使用控制器只有一次,你encuraged如果有应该出现在不止一个地方的特征使用相同的控制器在几个不同的地方.它只是意味着控制器不应该同时做几件事.

我们以照片库为例.虽然您可以创建一个获取所有照片的控制器,让您添加新照片,并允许您编辑和删除现有照片,这将是一个坏主意.如果您决定添加照片也可以从另一个页面"第X页"完成,该怎么办?如果您在何处重用同一个控制器,那么您还可以从服务器请求库,并为您不打算在该页面上设置的内容设置控件.

如果您改为制作一个仅负责获取内容的控制器,另一个用于添加新照片的控制器,另一个用于编辑的控制器等,那么这将很容易.您只需在"Page X"上实现create-controller,您就不必担心意外触发超出您想要的内容.您可以选择在页面上实现所需的功能,并且只选择该功能.这也使您的控制器小巧,易于阅读和快速编辑/修正,因此这是一个双赢的局面!

其次,如果您想将所有CRUD资源收集到一个对象(我也想做),它们不应该在控制器中,它们应该在服务中.所以你有一个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;
}]);
Run Code Online (Sandbox Code Playgroud)


Ste*_*wie 7

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

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

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

Rails中的CRUD/List路由 在此输入图像描述

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

AngularJS中的CRU/List路线 在此输入图像描述

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'
        });

    }
  ]
);
Run Code Online (Sandbox Code Playgroud)

控制器:

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();
    }

  }
]);
Run Code Online (Sandbox Code Playgroud)

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

选项2(使用resolve属性)

此选项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';}
          }
        });

    }
  ]
);
Run Code Online (Sandbox Code Playgroud)

控制器:

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();
    }

  }
]);
Run Code Online (Sandbox Code Playgroud)

这两种方法都需要在控制器中使用一些条件,但第二种方法至少要读得更清楚,因为在路由机制中解决了确切的操作,这会占用繁忙的控制器的一些逻辑.

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