1

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?

Rohan Büchner
  • 5,333
  • 4
  • 62
  • 106
yuro
  • 2,189
  • 6
  • 40
  • 76
  • see my answer below, please let me know if it makes sense to you. If you don't come right, I do have a small private git repo I can maybe share, but I'll need to anonymize it first. – Rohan Büchner Sep 14 '16 at 04:26
  • read this http://stackoverflow.com/questions/20801780/angular-ng-include-cshtml-page – User Sep 14 '16 at 04:28
  • for angular routing + MVC routing see ex. https://angularjsaz.blogspot.in/2015/10/angularjs-routing-aspnet-mvc-example.html – User Sep 14 '16 at 04:31
  • @RohanBüchner Thanks for your answer I will try your example and give you after that a feedback :) – yuro Sep 14 '16 at 07:07

2 Answers2

4

In addition to what @Andiih stated in his answer. The Angular routing library is meant to handle your Angular routes E.G. 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.

Current App

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

enter image description here

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.
Community
  • 1
  • 1
Rohan Büchner
  • 5,333
  • 4
  • 62
  • 106
  • 1
    Yes I totally agree with this solution if you want simple solution to your problem. – Datz Me Sep 14 '16 at 04:04
  • 1
    @DatzMe thanks. If you have any suggestions or improvements please let me know? :) I'm assuming not your down-vote, but I'm wondering why I received it :/ itmayormaynothavehurtmyegoalittle – Rohan Büchner Sep 14 '16 at 04:31
  • I don't really know why you have down vote without explanation. But I think as far as I know you got a point to solve it. The way I use angular in MVC is same as what you did too. Maybe the one who down vote you got a better solution :) – Datz Me Sep 14 '16 at 04:48
  • @RohanBüchner Do I need to remove the `RouteConfig.cs`? Because when I'm running the app I'm getting an 404 error message. – yuro Sep 14 '16 at 11:21
  • @yuro, no you still need to albeit just for the default route. – Rohan Büchner Sep 14 '16 at 11:59
  • @RohanBüchner Sorry, but my routing with ngRoute doesn't work :(.. Could you please make an example with ngRoute? It would be very helpful for me. – yuro Sep 14 '16 at 12:12
  • @yuro can you send me your git-hub name? If you want you can send it to me via my email. (email link @ the bottom of my blog: https://rohan-buchner.github.io/) – Rohan Büchner Sep 14 '16 at 13:21
  • @RohanBüchner Thanks for your help. I will send you an email after work! :) – yuro Sep 14 '16 at 13:30
  • @RohanBüchner Please check your emails :) – yuro Sep 15 '16 at 07:34
0

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 (i.e. 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.

Andiih
  • 12,285
  • 10
  • 57
  • 88
  • Thanks for your answer.. I would like to use the HTML5 routing of Angular. But somehow the routing doesn't work as expected. – yuro Sep 12 '16 at 17:51
  • 1
    Well said. I do get the impression that the OP needs a little clarity on the purpose of each of the routing frameworks `angular routing` / `net routing` (Also someone seems to have gone on a down-vote spree mid me answering as well, so not my down-vote) – Rohan Büchner Sep 14 '16 at 04:23