Happy Path Lifecycle
- A request is made to the server since the client hasn't loaded any resources yet when requesting content from a URL. ASP.NET routing picks up request and returns whatever content is bound for that route.
- If the index is returned (that contains the entrance point to the AngularJS App, the HTML content and subsequent resources are loaded from the server.
- AngularJS runs after receiving the DOM ready event
- AngularJS parses the DOM and begins executing the various components that it encounters.
Client-Side Routing
Once your application has loaded, client-side routing modules like ui-router ngRoute or the new component router will control the route for you and load content that is bound to that route. However, this only works properly if you always maintain to a pre-defined root route that you use to bootstrap your Angular application.
I.E. www.site.com/Home#/login
or www.site.com/Home!/login
if using $locationProvider.hashPrefix('!').html5Mode(true);
.
Fully reloading your root URL will load your main index content, re-initialize the angular application and pick up the corresponding client-side routing (which in this case would be the login route and module associated with that route). However, once you deviate from this, you run into unhappy path concerns.
Unhappy Path
If you request a URL that does NOT return the entrance point to the AngularJS application, Angular has no way to intercept the request and, subsequently will never run. As a result, if a request is being made to a URL that is not the entrance point to the Angular application, you have to decide how you want to handle it. A common approach is to redirect to Index.
Additional Questions
- Home/Index.cshtml - since you are blending both worlds, I'd recommend allowing Angular to control and drive all of the navigation once loaded. That would include calling partial routes and loading up the razor views as if they were templates (if you want to keep using Razor).
- Login/Index.cshtml - this is really tricky. One option is to detect whether or not they are calling this route from Ajax or not. If they are, allow the request. If not, redirect back to index since they're performing a full page load. An additional alternative would be to serve up the entire url, but you'd need to ensure that your angular app entrance point was also delivered. You could then work some magic after your angular app was loaded to load the main page content, then pop the modal afterwards. However, this would quickly get messy once you had additional situations of this nature.
- What if I have a page which has multiple lists in it? - Would you ever be SHOWING multiple lists at once? If so, that's fine, since you can actually load a parent state/template/controller that then includes additional child templates/controllers without needing to change the parent state. However, this question is incredibly broad and complex and really would need to be a separate, specific question.
What This Might Look Like
You would want to declare a route that handles your default entrance point:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "LoadMain", id = UrlParameter.Optional }
);
}
public class HomeController : Controller
{
public ActionResult LoadMain()
{
return View("Main.cshtml");
}
}
public class CategoriesController : Controller
{
public ActionResult Index()
{
return PartialView("Index.cshtml");
}
}
This way, if a user hit www.site.com
, we would return the content from /Home/LoadMain. This content would include all of our required Angular references (Angular, UI-Router, our main app, etc), which would then allow us to default our client-side route to /home and subsequently load /Home/Index
Main.cshtml:
<head>
<script src="//angular.js"></script>
<script src="//angular-ui-router.min.js"></script>
<script src="app.js"></script>
<body ng-app="app">
<div ui-view></div>
</body>
app.js
var app = angular.module('app', ['ui.router']);
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/Categories/Index' // corresponds to an MVC partial route
})
.state('login', {
url: '/login',
templateUrl: '/Login/Index' // corresponds to an MVC partial route
})
});