【问题标题】:AngularJS Restful RoutingAngularJS 宁静路由
【发布时间】:2013-02-05 21:22:41
【问题描述】:

我正在尝试使用 Restful/Ruby 约定 /<resource>/[method]/[id] 构建我的应用程序。我以前在使用像 CodeIgniter 这样的服务器端 MVC 框架时是如何根据 URI 动态路由的:

例如

www.foo.com/bar/baz/1

然后应用程序将在控制器/类bar 中使用方法baz 并返回views/bar/baz.php(填充来自bar->baz 的数据)

我想在 Angular 中做同样的事情,但我不确定它是否支持这一点(如果支持,我不确定该怎么做)。目前我正在使用$routeProviderwhen 方法来指定每种情况。 $location.path() 看起来可能有我需要的东西,但我认为我不能在 app.js 中使用它(config() 内)。

我想做的是这样的:

.config([
  '$routeProvider', function($routeProvider) {
    $routeProvider
    .when(//<resource> controller exists
      resource+'/'+method, {
        "templateURL": "views/" + resource + "/" + method + ".html",
        "controller":  resource
      }
    ).otherwise({ "redirectTo":"/error" });
  }
]);

并且路由器会自动调用相应的方法。

编辑另外,为什么当我指定when('/foo/bar', {…})$routeProvider 会发疯?

EDIT 2 根据 Lee 的建议,我正在考虑做这样的事情:

$routeProvider
  .when(
    '/:resource/:method/:id', {
      "templateUrl": function(routeParams){
        var path = 'views/'+routeParams.resource+'/';
        return ( typeof routeParams.method === 'undefined' ) ?
          path+'index.html' : path+routeParams.method+'.html';
      },
      "controller": RESOURCE
  })
  .otherwise({redirectTo: '/error'});

我在$routeProvider 的文档中注意到以下内容:

templateUrl – {string=|function()=} – 返回一个 ngView 应该使用的 html 模板的路径。

如果 templateUrl 是一个函数,它会被如下调用 参数:

• {Array.} - 从当前提取的路由参数 $location.path() 通过应用当前路由

编辑:将templateUrl 设置为函数的选项是不稳定的1.1.2 构建的一部分:#1963(但它在 2013 年 2 月 7 日起不起作用) .

有一个关于在 AngularJS 的 Github 上添加此功能的讨论:#1193#1524,但我无法确定它是否实际实现(在上面引用的 Dash 文档中,它看起来已经实现了,并且网站上的文档尚未更新)。

EDIT 3为了澄清我想要发生的事情(根据李的要求),简单来说,我想去www.foo.com/index.html#/people

Angular 应该使用控制器 people,自动调用它的 index 方法,并且应该提供服务

./views/people/index.html
./views/people/map.html

另外,如果我去www.foo.com/index.html#/people/map

Angular 应该再次使用people 控制器,但这次会自动调用它的map 方法并提供...map.html(因为在url 中指定了地图)

./views/people/index.html
./views/people/map.html

那么,如果我去

www.foo.com/index.html#/widgets

Angular 应该提供服务

./views/widgets/index.html
./views/widgets/details.html

路由器的代码应该非常通用——我不应该为每条路由指定.when()

【问题讨论】:

  • 有趣的是,我现在在文档中看不到这一点。可能是更新的版本。
  • 只是想了解您要完成的工作。我把它想象成/controller/template/id。你能举一个更具体的例子来说明你如何使用它吗?可能有一个更优雅的角度解决方案。
  • @Lee,是的,似乎 Dash 的文档版本比实际的文档站点更新(在我的问题中发布了屏幕截图)。对于您的第二条评论,我会在我的问题中添加更多内容(评论太多了)。
  • 顺便说一句 - 我在 rest url convnetions 上找到了这个 (microformats.org/wiki/rest/urls) 信息,并将其用作我的第二个答案的指南。这是您的想法还是您有指向您要模仿的特定规范的链接?

标签: javascript ruby-on-rails angularjs rest url-routing


【解决方案1】:

多考虑一下。对于那些通用的 CRUD/REST 类型的操作,您可以只使用一个控制器。然后使用资源和视图参数加载模板。

  • 创建
    • #/foo/create/0
    • 这有它自己的表单模板“/views/foo/create.html”和0 os 只是一个占位符。
    • 在提交时,您将调用控制器 ng-click="save()" 上的一个方法,该方法将在 POST "/rest/foo" 处发布到服务器。
  • 读取
    • #/foo/view/1
    • 同样,模板“/views/foo/view.html”只是数据的视图
    • 您可以使用 GET "/rest/foo/1" 调用服务方法从服务器获取数据
  • 更新 -#/foo/edit/1
    • 可以使用与 create 相同的模板,也可以根据需要使用不同的“/views/foo/edit.html”。
    • 还可以使用 GET "/rest/foo/1" 拉取数据
    • 使用 PUT "/rest/foo/1" 提交数据
  • 删除
    • #/foo/delete/1
    • 服务方法将调用 DELETE "/rest/foo/1"
    • 我认为您不需要哈希值,但您可以使用哈希值,因为控制器实际上可以进行验证或任何您想要确认删除的操作。也许有一个名为“/views/foo/delete.html”的视图询问您是否要删除记录。然后,您可以在某个按钮上使用 ng-click="delete(itemid)" 通过 ajax 删除项目。

所有这些都可以使用单个控制器/服务并动态生成服务和视图 url 来完成。

任何自定义的东西都需要自定义控制器、自定义路由和服务方法。我可能可以举一个例子,但今晚不行。

【讨论】:

  • 噢,REST 之神会因此而责备你:uri 不应该指定正在执行的操作(#/foo/view/1#/foo/get/1 相同)。 URI 应该是名词; HTTP 方法是动词。
  • 那么在您原来的问题“//[method]/[id]”中是“method”http 方法吗?我在考虑一个控制器方法。
  • 不,方法是控制器方法(我知道没有 HTTP 'map' 或 'people' 方法)
  • 我认为“方法”这个词意味着它是一个动词。无论是 http 请求还是控制器功能。所以理想情况下(在其他世界中)你不会在 url/route 中使用该方法,因为它是一个动词。似乎您可能想要/resource/template/id。根据我的经验,我通常使用视图中的 ng-click(或类似方法)在控制器上运行方法。创建控制器时,我通常也会运行一些默认方法。默认方法可能是基于参数的条件,但通常只有几个选项(例如:getList 或 getById)。
【解决方案2】:

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

编辑: 我发现了一些以前没有想到的有趣的东西。如果您在路由中省略控制器,它将使用模板中指定的控制器。因此,只要您用于给定控制器的所有模板都具有 ng-controller="resource" ,那么它将按预期为模板加载该控制器。当然,当前的路由实现没有可选参数,因此如果您有两个或三个参数,则需要指定单独的路由。最大的问题是它似乎调用了两次控制器方法。我猜这是因为同一个控制器有两个视图。但是,一个视图应该替换另一个视图,因此不应该有两个调用。这对我来说似乎是一个错误。我还发现了一些关于可能满足您需求的新路由系统的讨论,但可能还很遥远:https://github.com/angular-ui/router/issues?page=1&state=open。 github 上的示例现在使用以下方法,因此您可以根据需要浏览它。

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

还有模板:

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

现在在您的控制器中,您可以执行此操作以动态调用该方法。

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

/编辑

【讨论】:

  • 嗯,看起来很接近,谢谢!我在 templateUrl 的文档中看到:path or function that returns a path to an html template that should be used by ngView;我认为一个简单的功能可能会消除一些复杂性?说类似templateUrl: function() { return 'views/'+$routeParams.resource+'.html'; } 的内容(需要检查一下是否定义了 :method)。
【解决方案3】:

为了符合现有的路由系统(如 Rails),现在可以在路由中定义方法。 我创建了一个超级简单的解决方案,它允许路由根据路由定义和视图中的指令调用方法。我认为 ui-router 不是传统的,对于这样一个“应该是”的核心功能来说太复杂了。

该项目名为 ngMethod,位于:https://github.com/jzumbrun/ng-method

它的使用示例是:https://github.com/jzumbrun/chrome-apps-angularjs-bootstrap

如果我有这样的路线:

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

我在联系人/表单模板中有 ng-method:

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

然后 ng-method 将在 ContactsController 中调用 $scope.edit() 或 $scope.new()。 可以共享联系人/表单模板,并根据路由调用正确的方法 加载数据。这种风格现在更像是“Angularjs”,加载代码很像对模块和控制器的角度调用。

实现这一点的完整指令不到 20 行代码:

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

【讨论】:

    【解决方案4】:

    现在可以使用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';
            }
        });
    

    但是为了利用导航菜单上的$state.includes(),这可能会更好:

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

    上面可以通过循环来简化,从资源数组构建states($stateProvider 基本上支持添加状态whenever):

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

    警告 ui-router 并不真正支持可选状态参数 (planned for v0.4)

    【讨论】:

      猜你喜欢
      • 2011-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-29
      • 1970-01-01
      • 1970-01-01
      • 2015-06-21
      • 1970-01-01
      相关资源
      最近更新 更多