[To view earlier posts of the series – Click Here]
This will be short but very helpful post. I have seen lots of questions and confusion over Angular Routing and ASP.NET MVC Routing. In this post, I will try to unravel. We all already know the following two points
1- ASP.NET MVC is a server side technology so it means ASP.NET MVC routing takes place at Server.
2- AngularJS is a client side technology so Angular routing itself cannot conflict with ASP.NET MVC in any way. But it works on the top of the ASP.NET MVC. Let’s discuss it with a example.
I will be taking the sample from one of my earlier posts (Learning {{AngularJS}} with ASP.NET MVC – Part 5) on AngularJS and modify a bit for this post. Let me brief about the\example
1- It has one MVC controller – named EventsController that has three methods. One returns the index view and in rest of the two, one returns a list of talks and other, list of speakers in JSON format.
2- It has two angular routes, one for listing speakers (/Events/Speakers) and another from listing talks (/Events/Talks). These urls are used in top menu. So when we run the application and the url (http://localhost:/ or http://localhost:/events or http://localhost:/events/index) the page loads as
After clicking the the two tabs talks or speakers are loaded accordingly. Angular Routes in the sample are defined as
var eventModule = angular.module("eventModule", []).config(function ($routeProvider, $locationProvider) {
//Path - it should be same as href link
$routeProvider.when('/Events/Talks', { templateUrl: '/Templates/Talk.html', controller: 'eventController' });
$routeProvider.when('/Events/Speakers', { templateUrl: '/Templates/Speaker.html', controller: 'speakerController' });
$locationProvider.html5Mode(true);
});
Now if we access the direct angular defined routes (like ) it does not work. Let’s understand why?
When we access the url http://localhost:48551/events/index then it actually calls the MVC EventsController and Index action as expected and loads the page. Now when we click on one of the tabs (say speaker) then Angular chips in and it loads the angular view. The call does not go to MVC controller and action at server rather it gets handled at client side by Angular. It loads the data via angular service that initiates the ajax call to server to get the data. Here the url changes but handled at client side only and server (or say ASP.NET MVC) does not even get to know about it.
Now the next question, in second case, why the call does not go at server? In first case, there is nothing available at client side and request goes to the server and gets the page. When page loads it loads everything including angular library. Now when second time, the url gets changed Angular checks whether it can handle the url if yes then it just process it at client side and if not then try to load otherwise url if defined else loads nothing and leave it empty. The whole process can be defined it pictorially
Now we can imagine a scenario while accessing a Angular defined url (http://localhost:48551/Events/Talks) as initial request, it finds nothing at client side which can handle it as it is a fresh request. And it goes to server and tries to find the EventsController and Talks action but it is not defined by ASP.NET MVC so it does not work. We just have Index action at MVC controller. Now how to resolve this issue?
As Initially in this post, I pointed out that Angular defined urls are handled at Client side only, so AngularJS must be loaded and initialized before it can handle the request. There would be two steps involved
1- All angular defined urls should be mapped to a MVC route that returns the response to client with all the client side libraries including Angular.
2- Then as angular is initialized, it handles the url as discussed earlier When we try to access an angular defined urls as first request, it goes to server then we need to initialize the page first with AngularJS then rest angular takes care.It means that in the example, we need to call the index action when somebody try to access the AngularJS urls (Talks and Speakers) directly which will load the page and it will initialize the Angular as well then the Angular urls will be handled by angular itself. So how to do that, we just need to add some routes in Global.asax of so that even if Angular defined urls are called it returns fires of the index action only. So we can add it like
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "EventsCourses",
url: "Events/Talks",
defaults: new { controller = "Events", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "EventsSpeakers",
url: "Events/Speakers",
defaults: new { controller = "Events", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Events", action = "Index", id = UrlParameter.Optional }
);
}
So here I added two routes EventsCourses and EventsSpeakers for Angular Urls and for both, index action of events controller gets called.
Here we should not have a URL which can be processed by Angular and MVC both in that case you will get different behavior in both the case so we should keep in mind while defining the URLs. So I hope it must have been clear to you. Also you do not need to add each AngularURL as new route in Global.asax, you can have unique pattern in Angular URLs and for that pattern, you can have just one route which return the same action for that pattern. Sample is attached with the post.
[Update : Updated the flow diagram of this post and content to add some clarification in a scenario when requested url is not defined in URL – 04th May 15]
To navigate to previous and next post of the series, use the links at the bottom of the post.
Happy Learning
Brij