简体   繁体   中英

How to define Angular Routing in ASP.NET MVC

I'm a bit confused about using the routing of Angular within an Asp.Net MVC app.

To understand my question I will shown the currently code of my app:

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>

HomeController (ASP.NET):

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

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

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

My Angular module:

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

The output URL is shown as follows: localhost/Home/Index#/test1

Question:

Do I need to define Actions in HomeController and why? I thought I need just the ActionResult Index because I use PartialView()... And how can I achieve the following URL: localhost/test1 when I click on the Menu Test1?

In addition to what @Andiih stated in his answer. The Angular routing library is meant to handle your Angular routes EG between (for lack of a better term) front end controllers.

You seem to have mixed Angular and ASP.NET MVC routing in the way you're expecting the 2 to work. Your base route is localhost/Home/Index# from that point onward Angular appends its own (sub) routes to the base URL , resulting in the output URL that you're seeing.

Even though it might be feasible jumping between MVC and Angular routes and controllers as needed, it would be a more complicated scenario than what I'm assuming you're aiming to achieve. Also I've never needed to go to that extent in the past.

In summary what would currently happen:

If you had to navigate away from your Index.cshtml "Root" page you'd in effect navigate away from your current Angular app.. or navigate to a different app... depending on solution structure you could even use different JavaScript MV* framework for each a separate page... but that's just madness.

当前应用程序

...but I doubt it is what you want at this stage, so for simplicity sake lets stick to assuming you only have the one ASP.Net MVC Route / Page, and you just purely want to navigate between Angular routes and supply data to the Angular routes or pages from your back end .NET services somehow.

在此输入图像描述

Thus it seems that you could potentially be missing a link between what roles each of the frameworks are meant to play in the stack you currently have. (I'll try clarify)

For example:

If you look at a (very generic) overall solution structure that could look something like this below.

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

The RootView.cshtml gets constructed server-side, thus allowing you to use .NET bundling , Asp.NET Auth etc.. Anyhow, this whole page will act as a shell for the Angular app, the whole "front end app", thus runs within the context of that page.

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> 

You'll only need a single ASP.Net MVC Route , as I've stated before, you won't be navigating away from it (for this app at-least)

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

... but you will need multiple Angular routes.

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

You would then expose the other server-side logic or calls via WebAPI endpoints that you would need to consume via something like ngResource , which you will then utilize in your Angular controllers.

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

Note.

  1. Using this convention with angular .something(['service', function(service) { }]); is to make the code min safe . (So don't be too scared when you see it)
  2. I used a routing library called angular-ui-router in my example, I suggest that you have a look at it, if you have the freedom to choose at this stage... but the same principals would apply to an ngRoute solution.

It looks to me like you are a trying to get MVC to resolve the template URLs as partials. If so, then you can't use the same route for both the MVC route, and the Angular route.

To break this down into steps

1) Do you need MVC to resolve the template routes (ie have dynamically generated templates) If you do, make a template controller, and build each of your templates as partial actions on the template controller. If you don't need dynamic templates, just store your templates as static HTML in a folder such as /Content/Templates - which avoids the MVC pipline.

2) If you get 1) right, you should have a working example but with # routes. To get the HTML5 routes working, you need to exclude those routes from the MVC pipeline - routes.IgnoreRoute("/Index/*"); in RouteConfig.cs might work - I've not tried that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM