8

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

前任。

www.foo.com/bar/baz/1

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

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

我想做的是这样的:

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

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

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

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

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

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

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

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

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

有一个关于在 AngularJS 的 Github 上添加此功能的讨论:#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(因为在 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()为每个路由指定一个。

4

4 回答 4

5

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

  • 创造
    • #/foo/create/0
    • 这有它自己的表单模板“/views/foo/create.html”和 0 操作系统只是一个占位符。
    • 在提交时,您将调用控制器 ng-click="save()" 上的方法,该方法将在 POST "/rest/foo" 处发布到服务器。
    • #/foo/view/1
    • 同样,模板“/views/foo/view.html”只是数据的视图
    • 您可以使用 GET "/rest/foo/1" 调用服务方法从服务器获取数据
  • 更新 -#/foo/edit/1
    • 可以使用与创建相同的模板,或者如果您愿意,可以使用不同的“/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 删除该项目。

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

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

于 2013-02-08T05:54:50.997 回答
4

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

和模板:

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

/编辑

于 2013-02-06T03:40:02.083 回答
1

为了符合现有的路由系统(如 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 方法将在 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']]();
            }
        }
    };
}]);
于 2014-03-28T13:46:33.160 回答
0

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

但是为了利用$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从资源数组构建 s ($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

于 2014-02-11T19:36:56.787 回答