AngularJS Restful Routing

我正在尝试使用Restful / Ruby convension //[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(// controller exists resource+'/'+method, { "templateURL": "views/" + resource + "/" + method + ".html", "controller": resource } ).otherwise({ "redirectTo":"/error" }); } ]); 

并且路由器自动调用适当的方法。

编辑另外,当我指定when('/foo/bar', {…})时,为什么$ routeProvider会惊慌失措 ?

编辑2 Per 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的doc中注意到以下内容:

来自Dash的截图。全文如下

templateUrl – {string = | function()=} – 返回应由ngView使用的html模板路径的路径或函数。

如果templateUrl是一个函数,它将使用以下参数调用:

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

编辑 :将templateUrl设置为函数的选项是unstable 1.1.2 build的一部分: #1963 (但从2013-02-07开始不起作用)。

关于在AngularJS的Github上添加这个function的说法是: #1193 #1524 ,但我不知道它是否实际实现了(在上面引用的Dash文档中,它看起来已经存在,并且网站上的文档尚未更新)。

编辑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 (因为map是在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()

再考虑一下这个。 您可以为这些通用CRUD / REST类型操作安装一个控制器。 然后使用资源和视图参数加载模板。

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

所有这些都可以使用单个控制器/服务来完成,并动态生成服务和查看URL。

任何自定义您需要自定义控制器和自定义路由和服务方法。 我可能会把一个例子放在一起,但不是今晚。

这是github上的一个项目,它可以满足您的要求

编辑:我发现之前没有发生过一些有趣的事情。 如果在路径中省略控制器,它将使用模板中指定的控制器。 因此,只要您为给定控制器使用的所有模板都具有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/' }); }]); 

和模板:

 

resource1/one.html

{{r1data.selected}}

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

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

/编辑

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

该项目名为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:

 
...

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

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

上面的内容可以通过一个循环从一组资源构建state来简化( $ stateProvider支持基本上每次都添加状态):

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

警告 ui-router并不真正支持可选的状态参数( 计划用于v0.4 )