Using same controller for all CRUD operations (Rails-alike)

前端 未结 3 1095
没有蜡笔的小新
没有蜡笔的小新 2020-12-29 15:07

I have an angular controller that fetches a resource on creation:

angular.module(\'adminApp\')
  .controller(\'PropertiesCtrl\', function ($log, $scope, Pro         


        
3条回答
  •  攒了一身酷
    2020-12-29 16:05

    I see from your question (and from your SO tags) that you want to create a Rails-like controllers in AngularJS. Since both frameworks (Rails and AngularJS) share a similar MVC principle this is actually quite easy to accomplish.

    Both frameworks allow you to instruct different routes to use the same controller.

    In Rails, your usual index/show/new/edit/destroy methods (actions) are provided out of the box (with scaffolding). These default actions are mapped to different, well established routes and HTTP methods.

    CRUD/List routes in Rails enter image description here

    Now, in AngularJS applications (or all SPAs for that matter) you need only a subset of these routes, because client-side routing understands only GET requests:

    CRU/List routes in AngularJS enter image description here

    AngularJS natively does not provide a scaffolding mechanism that would generate all your CRUD routes for you. But nevertheless it provides you with at least two different ways of wiring up your CRUD/List routes with a single controller.

    Option 1 (Using $location.path())

    Using location.path() method you can structure your PhotosCtrl to do different things depending on, well, location path.

    Routes:

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

    Controller:

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

    These four ifs makes the controller look a bit messy, but when replacing 4 different controllers with one, few conditionals are inevitable.

    Option 2 (Using resolve property)

    This option employes the resolveproperty of the route configuration object to produce different 'action identifier' for different routes.

    Routes:

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

    Controller:

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

    Both methods require using some conditionals in your controller, but the second method is at least a bit clearer to read, because the exact action is resolved inside the routing mechanism, which takes some logic off of your busy controller.

    Of course, in any real-world application you'll probably have many more methods defined inside the controller, in which case your controller might get quite unreadable. These examples use a simple $resource instance (Phones) which relies on a simple RESTfull backend API (Rails?). But, when your view logic becomes complex you will probably want to employ Angular services/factories in order to abstract some of the code in your controllers.

提交回复
热议问题