javascript AngularJS 静态路由

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14717183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 22:23:27  来源:igfitidea点击:

AngularJS Restful Routing

javascriptruby-on-railsangularjsresturl-routing

提问by jacob

I'm trying to structure my app using the Restful/Ruby convension /<resource>/[method]/[id]. How I've done it previously when using a server-side MVC framework like CodeIgniter was to dynamically route based on the URI:

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

ex.

前任。

www.foo.com/bar/baz/1

The app would then use method bazin controller/class barand return views/bar/baz.php(populated with data from bar->baz)

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

I would like to do the same in Angular, but I'm not sure if it supports this (and if it does, I'm not sure exactly how to go about it). At the moment I'm using $routeProvider's whenmethod to specify each case. $location.path()looks like it might have what I need, but I don't think I can use it in app.js(within config()).

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

What I'd like to do is something like this:

我想做的是这样的:

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

And the router automatically calls the appropriate method.

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

EDITAlso, why does $routeProviderfreak out when I specify when('/foo/bar', {…})?

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

EDIT 2Per Lee's suggestion, I'm looking into doing something like this:

编辑 2Per 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'});

I noticed the following in $routeProvider's doc:

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

screenshot from Dash. full text below

来自 Dash 的屏幕截图。 全文如下

templateUrl– {string=|function()=} – path or function that returns a path to an html template that should be used by ngView.

If templateUrlis a function, it will be called with the following parameters:

? {Array.<Object>} - route parameters extracted from the current $location.path()by applying the current route

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

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

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

Edit: The option to set templateUrlto a function is part of the unstable 1.1.2build: #1963(but it doesn't work as of 2013-02-07).

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

There is a dicussion about adding this functionality on AngularJS's Github: #1193#1524, but I can't tell if it was actually implemented (in the docs from Dash quoted above, it looks like it has been, and the docs on the site haven't been updated yet).

有一个关于在 AngularJS 的 Github 上添加这个功能的讨论:#1193 #1524,但我不知道它是否真的实现了(在上面引用的 Dash 的文档中,它看起来已经实现了,并且站点上的文档还没更新)。

EDIT 3To clarify what I want to happen (per lee's request), in simplest terms, I would like to go to www.foo.com/index.html#/people

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

Angular should use controller people, automatically call its indexmethod, and should serve up

Angular 应该使用 controller people,自动调用它的index方法,并且应该服务起来

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

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

Also, if I go to www.foo.com/index.html#/people/map

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

Angular should use the peoplecontroller again, but this time automcatically call its mapmethod and serve up …map.html(because map was specified in the url)

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

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

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

Then, if I go to

那么,如果我去

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

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

Angular should serve up

Angular 应该服务起来

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

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

The code for the router should be very generic—I shouldn't have to specify a .when()for every route.

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

采纳答案by jacob

This is now possible with ui-router0.2.8:

现在可以使用ui-router0.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:

但是为了利用$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';
        }
    });

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

可以使用循环简化上述内容,以state从资源数组构建s($stateProvider基本上支持在任何时候添加状态):

var resources = [ 'r1', 'r2', '…' ];

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

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

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

回答by Lee

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.

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

  • 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.
  • 创建
    • #/foo/创建/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/删除/1
    • 服务方法将调用 DELETE "/rest/foo/1"
    • 我不认为你想要一个散列,但你可以使用一个散列,因为控制器实际上可以进行验证或任何你想确认删除的事情。也许有一个名为“/views/foo/delete.html”的视图,询问您是否要删除记录。然后,您可以在某个按钮上使用 ng-click="delete(itemid)" 通过 ajax 删除项目。

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

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

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.

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

回答by Lee

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

这是 github 上的一个项目,它的功能与您所要求的很接近

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.

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

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

/编辑

回答by Jon

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.

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

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

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

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

它的使用示例是: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:

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

<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.

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

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

实现这一点的完整指令不到 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']]();
            }
        }
    };
}]);