AngularJS Restful Routing

后端 未结 4 1385
滥情空心
滥情空心 2021-01-05 02:44

I\'m trying to structure my app using the Restful/Ruby convension //[method]/[id]. How I\'ve done it previously when using a server-side MVC fra

相关标签:
4条回答
  • 2021-01-05 02:56

    Here is a project on github that does something close to what you are asking

    EDIT: I discovered something interesting that had not occurred to me before. If you leave out the controller in the route it will use the controller specified in the template. So as long as all the templates that you use for a given controller have ng-controller="resource" then it will load that controller for the template as expected. Of course with the current implementation of routes there are no optional parameters, so if you have two or three parameters you would need to specify a separate route. Biggest problem is it appears to call the controller method twice. I am guessing this is because there are two views with the same controller. However one view should replace the other so there should not be two calls. This seems like a bug to me. I also found some discussion of a possible new routing system in the works that may meet your needs, but it may be pretty far off: https://github.com/angular-ui/router/issues?page=1&state=open. The sample on github is now using the following method so you can browse that if you like.

    var restrouteApp = angular.module('restrouteApp', [])
      .config(['$routeProvider', function($routeProvider) {
        $routeProvider
          .when('/:ctrl/:method', {
            templateUrl: function(rp){
              if(!rp.method) {rp.method = 'index';}
              console.log('route one'); 
              return 'views/'+rp.ctrl+'/'+rp.method+'.html';
            }
          })
          .when('/:ctrl/:method/:id', {
            templateUrl: function(rp){
              if(!rp.method) {rp.method = 'index';}
              console.log('route two'); 
              return 'views/'+rp.ctrl+'/'+rp.method+'.html';
            }
          })
          .otherwise({
            redirectTo: '/resource1/'
          });
      }]);
    

    And the templates:

    <div ng-controller="resource1">
      <h1> resource1/one.html </h1>
      <div>{{r1data.selected}}</div>
    </div>
    

    Now in your controller you can do this to call the method dynamically.

    restrouteApp.controller('resource1', function($scope,$routeParams,$log,Resource1Service) {
    
      $log.info('new resource1');
    
      $scope.controllername = $routeParams.ctrl;
      $scope.r1data= Resource1Service.shared;
    
      $scope.index = function(){
        Resource1Service.index().then(function(){
          //when the service returns
        });
      }
    
      $scope.one = function(){
        $scope.r1data.selected = $scope.r1data.resources[0];
      }
      $scope.two= function(){
        $scope.r1data.selected = $scope.r1data.resources[1];
      }
    
      //call the specified method of this controller 
      $scope[$routeParams.method]();
    });
    

    /EDIT

    0 讨论(0)
  • 2021-01-05 02:58

    To conform to existing routing systems like Rails, the ability to define the method in the route is now available. I created a super simple solution that allows routes to call a method based on the route definition and a directive in the view. I think ui-router is not conventional and is too complicated for a such a "should be" core feature.

    The project is called ngMethod and is located at: https://github.com/jzumbrun/ng-method.

    An example of its use is: https://github.com/jzumbrun/chrome-apps-angularjs-bootstrap

    So if I have a route like so:

    $routeProvider.
            when('/contacts/new', {
                controller: 'ContactsController',
                method: 'new',
                templateUrl: $configProvider.template('contacts/form.html'),
            });
    
        $routeProvider.
            when('/contacts/:id/edit', {
                controller: 'ContactsController',
                method: 'edit',
                templateUrl: $configProvider.template('contacts/form.html'),
            });
    

    and I have ng-method in the contacts/form template:

    <div class="col-lg-12" ng-method>
        <form role="form">
    ...
    

    Then the ng-method will call either $scope.edit() or $scope.new() in the ContactsController. Than the contacts/form template can be shared, and depending on the route call the correct method to load the data. This style is now more "Angularjs" and the loading the code is much like angular calling to modules and controllers.

    The full directive that makes this happen is less than 20 lines of code:

    app.directive('ngMethod', ['$route', function($route) {
        return {
            // Restrict it to be an attribute in this case
            restrict: 'A',
            // responsible for registering DOM listeners as well as updating the DOM
            link: function(scope, element, attrs) {
    
                // Call method without params. Use $routeParams
                if(angular.isFunction(scope[attrs.ngMethod])){
                    scope[attrs.ngMethod]();
                // default to the route method if attrs.ngMethod is empty
                } else if(angular.isObject($route.current) 
                    && angular.isString($route.current['method']) 
                    && angular.isFunction(scope[$route.current['method']])){
                    scope[$route.current['method']]();
                }
            }
        };
    }]);
    
    0 讨论(0)
  • 2021-01-05 03:14

    Thinking about this a little more. You could just have a single controller for those generic CRUD/REST type operations. Then load the templates using the resource and view parameters.

    • Create
      • #/foo/create/0
      • This has it's own form template "/views/foo/create.html" and the 0 os just there for a placeholder.
      • on submit you would call a method on the controller ng-click="save()" which would post to the server at POST "/rest/foo".
    • Read
      • #/foo/view/1
      • Again the template "/views/foo/view.html" is just a view of the data
      • You can call a service method to get the data from your server using GET "/rest/foo/1"
    • Update -#/foo/edit/1
      • Could use the same template as create or you could use a different one "/views/foo/edit.html" if you like.
      • Also pull the data using GET "/rest/foo/1"
      • Submit the data using PUT "/rest/foo/1"
    • Delete
      • #/foo/delete/1
      • service method would call DELETE "/rest/foo/1"
      • I don't think you want a hash for this, but you could use one because the controller could actually do a verification or anything you like to confirm the deletion. Maybe have a view called "/views/foo/delete.html" that asks if you want to delete the record. Then you could have ng-click="delete(itemid)" on a button somewhere that deletes the item via ajax.

    All this could be done using a single controller/service and dynamically generating the service and view urls.

    Anything that's custom you would need a custom controller and custom routes and service methods for. I could probably throw together an example, but not tonight.

    0 讨论(0)
  • 2021-01-05 03:19

    This is now possible with ui-router 0.2.8:

    $stateProvider
        .state('base', {
            url: '/:resource/:collection/:id',
            controllerProvider: function( $stateParams )
            {   // assuming app.controller('FooCtrl',[…])
                return $stateParams.collection + 'Ctrl';
            },
            templateUrl: function( $stateParams )
            {
                return '/partials/' + $stateParams.collection + '.html';
            }
        });
    

    But in order to take advantage of $state.includes() on nav menus, this would probably be better:

    $stateProvider
        .state('base.RESOURCE_NAME1', {
            url: '/:collection/:id',
            controllerProvider: function( $stateParams )
            {   // assuming the convention FooCtrl
                return $stateParams.collection + 'Ctrl';
            },
            templateUrl: function( $stateParams )
            {
                return '/partials/' + $stateParams.collection + '.html';
            }
        }).state('base.RESOURCE_NAME2', {
            url: '/:collection/:id',
            controllerProvider: function( $stateParams )
            {   // assuming the convention FooCtrl
                return $stateParams.collection + 'Ctrl';
            },
            templateUrl: function( $stateParams )
            {
                return '/partials/' + $stateParams.collection + '.html';
            }
        });
    

    The above could be simplified with a loop to build the states from an array of resources ($stateProvider supports adding states basically whenever):

    var resources = [ 'r1', 'r2', '…' ];
    
    for ( var r = resources.length-1; r >=0; r-- )
    {
        var name = resources[r];
        $stateProvider.state('base.'+name, {
            …
        });
    }
    

    Caveat ui-router doesn't not really support optional state parameters (planned for v0.4)

    0 讨论(0)
提交回复
热议问题