如何在 ASP.NET MVC 中定义 Angular 路由

How to define Angular Routing in ASP.NET MVC

我对在 Asp.Net MVC 应用程序中使用 Angular 的路由有点困惑。

为了理解我的问题,我将显示我的应用程序的当前代码:

Index.cshtml:

<head>
    ...
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
</head>
<body>
    <div class="navbar navbar-default navbar-fixed-top">
        <div class="container">
            <div class="navbar-collapse collapse glyphicons pull-right">
                <ul class="nav navbar-nav">
                    <li ng-class="{active:isActive == '/test1'}">
                        <a href="#/test1">Test1</a>
                    </li>
                    <li ng-class="{active:isActive == '/test2'}">
                        <a href="#/test2">Test2</a>
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        <div ng-view></div>
    </div>

    @Scripts.Render("~/bundles/angular")
    @Scripts.Render("~/bundles/app")
</body>

家庭控制器 (ASP.NET):

namespace Tests.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return PartialView();
        }

        public ActionResult Test1()
        {
            return PartialView();
        }

        public ActionResult Test2()
        {
            return PartialView();
        }
    }
}

我的Angular模块:

angular
   .module('Tests', [
      'ngRoute'
   ])
   .config(config)
   .run(['$rootScope', '$route', '$location', function($rootScope, $route, $location) {
      $rootScope.$on('$routeChangeSuccess', function(event, currRoute, prevRoute) {
         $rootScope.title = $route.current.title;
      });

      var path = function() {
         return $location.path();
      };

      $rootScope.$watch(path, function(newPath, oldPath) {
         $rootScope.isActive = newPath;
      });
   }]);

config.$inject = ['$routeProvider'];

function config($routeProvider) {
   $routeProvider
      .when('/test1', {
         templateUrl: '/Home/Test1',
         isActive: 'test1'
      })
      .when('/test2', {
         templateUrl: '/Home/Test2',
         isActive: 'test2'
      })
      .otherwise({
         redirectTo: '/'
      });
}

输出URL如下所示:localhost/Home/Index#/test1

问题:

我需要在 HomeController 中定义 Actions 吗?为什么?我以为我只需要 ActionResult 索引,因为我使用 PartialView()... 我怎样才能实现以下 URL: localhost/test1 当我点击 Menu Test1?

在我看来,您似乎在尝试让 MVC 将模板 URL 解析为部分网址。如果是这样,那么您不能对 MVC 路由和 Angular 路由使用相同的路由。

将其分解为步骤

1) 您是否需要 MVC 来解析模板路由(即动态生成的模板)如果需要,请制作一个模板控制器,并将每个模板构建为模板控制器上的部分操作。如果您不需要动态模板,只需将您的模板作为静态 HTML 存储在诸如 /Content/Templates 之类的文件夹中 - 这样可以避免 MVC 管道。

2) 如果 1) 正确,您应该有一个工作示例,但带有 # 路由。要使 HTML5 条路由正常工作,您需要从 MVC 管道中排除这些路由 - routes.IgnoreRoute("/Index/*")​​; in RouteConfig.cs 可能有效 - 我没试过。

除了@Andiih 在他的回答中所说的。 Angular 路由库旨在处理您的 Angular 路由 E.G.在 (找不到更好的术语) 前端 控制器之间。

您似乎以您期望 2 工作的方式混合了 AngularASP.NET MVC 路由。你的基本路线是 localhost/Home/Index# 从那一点开始 Angular 将它自己的(子)路线附加到基本 URL,导致你看到的输出 URL

尽管根据需要在 MVCAngular 路由和控制器之间跳转可能是可行的,但这将是一个比我假设您要实现的目标更复杂的场景。而且我过去从来不需要达到那种程度。

总结当前会发生什么:

如果您必须离开 Index.cshtml "Root" 页面,您实际上会离开当前的 Angular 应用..或导航 不同的应用程序...根据解决方案结构,您甚至可以为每个单独的页面使用不同的 JavaScript MV* 框架...但这太疯狂了。

...但我怀疑这是你现阶段想要的,所以为了简单起见,我们坚持假设你只有一个 ASP.Net MVC Route / 页面,你只是想在 Angular 以某种方式从后端 .NET 服务向 Angular 路由或页面提供数据。

因此,您似乎可能会遗漏 link 每个框架在您当前拥有的堆栈中所扮演的角色。 (我会尽力澄清)

例如:

如果您查看(非常通用的)整体解决方案结构,可能如下所示。

    - css
       - bootstrap.css        // all 3rd party css here
    - libs
       - angular.js           // all 3rd party js libs here
       - angular-ui-router.js
    - controllers
       - RootController.cs    // This is your HomeController. 
                              // It's effectively used to "deliver" the root view which is
                              // your core/base page for lack of a better term
    - views 
       - RootView.cshtml      // see code RootView.cshtml below, 
                              // this defines your app "layout"
                              // as well as where you deliver your bundles,
                              // make use of server side auth, etc..
    - webapi
       - test1Api.cs          // access to test1 data or serverside process
       - test2Api.cs          // etc..
    - app
      - pages
          - home                 
          - test1
             - test1Ctrl.js
             - test1View.html
             - test1.css
          - test2
             - test2Ctrl.js
             - test2View.html
             - test2.css
      - services
         - someRandomSvc.js     
         - resourceSvc.js   // this is where you consume your 
                            // .NET back end API. E.G: test1Api & test2Api, etc..
      - app.js
      - app.routes.js
      - etc...

RootView.cshtml 在服务器端构建,因此允许您使用 .NET bundlingAsp.NET Auth 等。无论如何,整个页面将充当 shell对于 Angular 应用程序,整个 "front end app" 因此在该页面的上下文中运行。

RootView.cshtml

<head>
    @Styles.Render("~/content/3rdPartyCSS")
    @Styles.Render("~/content/appSpecificCSS")
</head>
<body>
    <div class="navbar navbar-default navbar-fixed-top">
    <div class="container">
        <div class="navbar-collapse collapse glyphicons pull-right">
            <ul class="nav navbar-nav">
                <li><a ui-sref="home">Home</a></li>
                <li><a ui-sref="test1">Test1</a></li>
                <li><a ui-sref="test2">Test2</a></li>
            </ul>
        </div>
    </div>
    </div>
    <div class="container body-content">
        <div ng-view></div>
    </div>

    @Scripts.Render("~/bundles/3rdPartyJS")
    @Scripts.Render("~/bundles/appSpecificJS")
</body> 

你只需要一个 ASP.Net MVC Route,正如我之前所说,你不会离开它(至少对于这个应用程序)

RootController.cs

namespace Tests.Controllers
{
    public class RootController : Controller
    {
        public ActionResult RootView()
        {
            // as I've mentioned previously in another post, 
            // we're returning a partial to avoid the usage of _layout.cshtml 
            // as our RootView is acting as the layout in for the angular app.          
            // (it would introduce another level of not needed nesting)
            return PartialView();
        }
    }
}

...但您将需要多条 Angular 路线。

app.routes.js

var app = angular.module('Tests');
app.config(['$stateprovider', function($stateprovider) {     
  $stateprovider
     .state('home', { url: '', templateUrl: 'app/pages/home/homeView.html' }])
     .state('test1', { url: '/test1', templateUrl: 'app/pages/test1/test1View.html'})
     .state('test2', { url: '/test2', templateUrl: 'app/pages/test2/test2View.html'});    
}]);

然后您将通过 WebAPI endpoints that you would need to consume via something like ngResource 公开其他服务器端逻辑或调用,然后您将在 Angular 控制器中使用它们。

testXView.html

<div ng-controller="testXCtrl as test">{{ test.something }}</div>

testXCtrl.js

var app = angular.module('Test');
app.controller('testXCtrl',['resourceSvc', function(resourceSvc) {
   resourceSvc.getSomeData().then(function(data) {
       this.something = data;  //.. or whatever floats your boat :P
   })
}]);

注.

  1. 将此约定与angular .something(['service', function(service) { }]); 一起使用是使代码min safe。 (所以看到它的时候不要太害怕)
  2. 我在我的例子中使用了一个名为angular-ui-router的路由库,我建议你看看它,如果你在这个阶段可以自由选择......但是同样的原则也适用于ngRoute 解决方案。