AngularJS/MVC Cookbook Simple Routing

In a classic ASP.NET MVC application, stepping between parts of the application usually requires a trip to the web server to refresh the entire contents of the page. However, in a Single Page Application (SPA), only elements within the page are updated giving the user a better, more responsive experience. A SPA implementation may still make transitions to other sections of the application with a full page refresh, but generally a single page manages the application’s functionality through dynamic views and web service (AJAX) calls.

AngularJS supports dynamic views through routes and the ngView directive. Your MVC application would provide the partial views that the ngView directive will dynamically load. The Simple Routing recipe of the AngularJS MVC Cookbook provides an example of setting up routes and displaying dynamic views.

In this example, the views, called Home, About, and Contact, are simple partial views rendered by the MVC controller.

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

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

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

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

The Index page is the “container” page that will host the dynamic views. It has the responsibility of loading the initial Angular configuration, including any required javascript files. The ngApp directive (“myApp” in this example) kicks off the Angular bootstrap process

Routes are configured on an Angular module. They specify properties of each route, including the view and controller to use:

angular
    .module('myApp', [
        'myApp.ctrl.home',
        'myApp.ctrl.contact',
        'myApp.ctrl.about'
    ])
    .config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
        
        // Specify the three simple routes ('/', '/About', and '/Contact')
        $routeProvider.when('/', {
            templateUrl: '/Home/Home',
            controller: 'homeCtrl',
        });
        $routeProvider.when('/About', {
            templateUrl: '/Home/About',
            controller: 'aboutCtrl',
        });
        $routeProvider.when('/Contact', {
            templateUrl: '/Home/Contact',
            controller: 'contactCtrl'
        });
        $routeProvider.otherwise({
            redirectTo: '/'
        });

        // Specify HTML5 mode (using the History APIs) or HashBang syntax.
        $locationProvider.html5Mode(false).hashPrefix('!');

    }]);

Routes are used by the ngView directive which has the responsibility of loading the route’s content. As the routes change, the content gets automatically updated.

 

If you run the application, watch the network activity for the browser.  You’ll notice that the initial page will load, including the “Home” dynamic view.  Then switching to the About and Contact views will load these views once, but after that, no longer make trips to the server. By this, you can see how the user will get a more dynamic, responsive experience.

As I mentioned previously, AngularJS was built from the ground up with testing in mind. It’s not an afterthought. So in that spirit, let’s look at how we might test the functionality of this example.

Dave Baskin

View Comments

  • Dave,
    thank you very much for your samples they are a great help!
    All of them are working fine in Visual Studio 2013 and a real eye openers.
    However, I got a problem using Simple Routing pattern in production IIS. Under the Visual Studio links are http://localhost:11002/#!/Contact. In production environment, links include application name http://prouctionserver/simplerouting/#!/Contact, and both MVC and Angular navigation in 'Simple Routing' silently fails (ng-view div is not get populated).
    Can you suggest how you code can be modified to accommodate such scenarios?

  • I was able to make routing work for both local VS IIS and production IIS by removing leading '/' from templateURL property.
    $routeProvider.when('/', {
    templateUrl: 'Home/Home',

  • Hello Dave,
    Thanks for a nice article.
    I am find it difficult to route for Areas. I have a Client/Home/ Index controller/ Action in area Client.
    Index has following code:
    Insurance
    Sentiment
    Broker
    Route:
    var configFunction = function ($routeProvider, $httpProvider) {
    var viewBase = '/Client/Profile/';
    $routeProvider
    .when('/Client/Profile/#/Insurance', {
    templateUrl: function (params) { alert(params.customer360Id); return viewBase + 'Insurance?customer360Id=' + params.customer360Id; },
    controller: HomeController
    })
    .when('/Sentiment', {
    templateUrl: '/Profile/Sentiment',
    controller: HomeController
    })
    .when('/Broker', {
    templateUrl: '/Profile/Broker'
    });
    //$locationProvider.html5Mode(false).hashPrefix('!');
    }
    ClientRegistration:
    public override void RegisterArea(AreaRegistrationContext context)
    {
    if(context == null)
    throw new ArgumentNullException("context");
    context.MapRoute(
    "Client_default",
    "Client/Home/Index/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    namespaces: new[] { "Customer360.Controllers" });
    context.MapRoute(
    "Client_Insurance",
    "Client/Profile/Insurance",
    new { controller = "Profile", action = "Insurance", id = UrlParameter.Optional },
    namespaces: new[] { "Customer360.Controllers" });
    It does not work.

  • Hello Dave,
    Thanks for a nice article.
    I am find it difficult to route for Areas. I have a Client/Home/ Index controller/ Action in area Client.
    Index has following code:
    Insurance
    Sentiment
    Broker
    Route:
    var configFunction = function ($routeProvider, $httpProvider) {
    var viewBase = '/Client/Profile/';
    $routeProvider
    .when('/Client/Profile/#/Insurance', {
    templateUrl: function (params) { alert(params.customer360Id); return viewBase + 'Insurance?customer360Id=' + params.customer360Id; },
    controller: HomeController
    })
    .when('/Sentiment', {
    templateUrl: '/Profile/Sentiment',
    controller: HomeController
    })
    .when('/Broker', {
    templateUrl: '/Profile/Broker'
    });
    //$locationProvider.html5Mode(false).hashPrefix('!');
    }
    ClientRegistration:
    public override void RegisterArea(AreaRegistrationContext context)
    {
    if(context == null)
    throw new ArgumentNullException("context");
    context.MapRoute(
    "Client_default",
    "Client/Home/Index/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    namespaces: new[] { "Customer360.Controllers" });
    context.MapRoute(
    "Client_Insurance",
    "Client/Profile/Insurance",
    new { controller = "Profile", action = "Insurance", id = UrlParameter.Optional },
    namespaces: new[] { "Customer360.Controllers" });
    It does not work.

  • Nice article, but I found one problem with it. If you have breaks points actions(Home, About and Contact) when page loads first time when you click on respective Home, About and Contact it is hitting breaks. Once you have clicked all the buttons and try again break points are not hitting. Is there any problem.

  • Nice article, but I found one problem with it. If you have breaks points actions(Home, About and Contact) when page loads first time when you click on respective Home, About and Contact it is hitting breaks. Once you have clicked all the buttons and try again break points are not hitting. Is there any problem.

  • Hi, this is nice article ofcourse but i am bit new in angular kindly explain me how i can work with below scnenario:
    this is modal pop which render the mvc razor partial view
    @{Html.RenderPartial("UploadFiles");}
    with in the page this page
    div id="RCConetent" class="row" ng-app="Reg" ng-controller="RequestCondition">

    @* Today's Stats*@
    طلب للحصول
    '@ViewBag.ScreenTitle'

    @*A fully responsive premium quality admin template built on Twitter Bootstrap by EGrappler.com. These are some dummy lines to fill the area.
    851

    423

    922

    25%

    *@
    1
    أهلا وسهلا!
    2
    حدد مزرعة
    3
    شروط وإجراءات الحصول على الخدمات
    @*
    4
    إجراء الخدمة
    *@
    5
    ارسل طلب
    @{Html.RenderPartial("UploadFiles");}

    @**@
    شروط الخدمة
    S.No.
    وصف
    الفئة المسؤولة
    إرفاق المستندات
    الحالة
    @*
    *@
    @*
    Row
    First Name
    Last Name
    Email
    *@
    {{item.ROWNUM}}
    {{item.DESCRIPTION}}
    {{item.CATEGORYDESCRIPTION}}

    Required Attached
    @*
    2
    Peter
    Parker
    peterparker@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    *@
    @*href="@Url.Action("RegistrationVerification", "Account", new { step = "registration request" })"*@





    with javascript below
    @section Scripts {
    $(document).ready(function () {
    //$('#adeel').chosen();
    function rowStyle(row, index) {
    var classes = ['active', 'success', 'info', 'warning', 'danger'];
    if (index % 2 === 0 && index / 2 < classes.length) {
    return {
    classes: classes[index / 2]
    };
    }
    return {};
    }
    var Reg = angular.module('Reg', [
    'Reg.ctrl.popup'
    ]).config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
    // Specify the three simple routes ('/', '/About', and '/Contact')
    $routeProvider.when('/', {
    templateUrl: '/Shared/UploadFiles',
    controller: 'homeCtrl',
    });
    $routeProvider.otherwise({
    redirectTo: '/'
    });
    // Specify HTML5 mode (using the History APIs) or HashBang syntax.
    $locationProvider.html5Mode(false).hashPrefix('!');
    }]);
    Reg.controller('RequestCondition', function ($scope, $http) {
    $scope.ServiceConditions = [];
    $scope.LoadServiceCondidence = function () {
    $http({
    url: path + "/Service/GetRequestConditions",
    method: "GET"
    }).success(function (data, status, headers, config) {
    // console.log(data);
    angular.copy(data.dataset, $scope.ServiceConditions);
    console.log($scope.ServiceConditions)
    $.each($scope.ServiceConditions, function (key, value) {
    // console.log(value);
    });
    }).error(function (data, status, headers, config) {
    //something went wrong
    //alert('Error getting data');
    });
    };
    $scope.LoadServiceCondidence();
    $scope.ClickUpload = function (data) {
    console.log(data);
    // alert("hi");
    // data.DESCRIPTION = 'aaaaaaaaaaa';
    $("#popUpUploadFiles").css("display", "block");
    //$("#popUpUploadFiles").removeClass('fade');
    data.ISATTACHED = 1;
    };
    $scope.onFileSelect=function(files)
    {
    console.log(files);
    }
    $scope.MoveFarward = function () {
    window.location.href = "@Url.Action("SubmitRequest", "Service" , null)";
    };
    });
    angular.bootstrap(document.getElementById("RCConetent"), ['Reg']);
    });
    when i render the partial view which contains the following js and html
    ×
    Thank you for visiting EGrappler.com
    {{vm.title}}
    Name
    Size (kb)
    Created on
    Modified on
    no photos
    {{photo.Name}}
    {{photo.Size}}
    {{photo.Created | date: short}}
    {{photo.Modified | date: short}}
    Select and upload new photos
    Files:
    {{photo.name}}
    (function() {
    "use strict";
    /**
    * @ngdoc function
    * @name webApiSample.controller:HomeCtrl
    * @description
    * # HomeCtrl
    * Controller of the webApiSample
    */
    angular
    .module("Reg")
    .controller("popup", ["$window",
    "fileService", "Upload", "apiUrl", function($window, fileService, Upload, apiUrl) {
    var vm = this;
    //Variables
    vm.photos = [];
    vm.files = [];
    vm.previewPhoto = {};
    vm.spinner = {
    active: true
    };
    //Functions
    function setPreviewPhoto(photo) {
    vm.previewPhoto = photo;
    }
    function activate() {
    vm.spinner.active = true;
    fileService.getAll()
    .then(function (data) {
    vm.photos = data.Photos;
    vm.spinner.active = false;
    setPreviewPhoto();
    }, function(err) {
    console.log("Error status: " + err.status);
    vm.spinner.active = false;
    });
    }
    function uploadFiles(files) {
    Upload.upload({
    url: apiUrl,
    data: { file: files }
    })
    .then(function(response) {
    activate();
    setPreviewPhoto();
    }, function(err) {
    console.log("Error status: " + err.status);
    vm.spinner.active = false;
    });
    $window.location.reload();
    }
    function removePhoto(photo) {
    fileService.deletePhoto(photo.Name)
    .then(function() {
    activate();
    setPreviewPhoto();
    });
    }
    //Set scope
    activate();
    vm.uploadFiles = uploadFiles;
    vm.remove = removePhoto;
    vm.setPreviewPhoto = setPreviewPhoto;
    }
    ]);
    })();
    it is throwing error that "popup " function is not find.
    my question are how to use the mvc partial control like i have in the page of mvc like i am using with angular js if you see code you will understand my scnerio clearly.
    thanks in advance and pardon for my bad english :)

  • Hi, this is nice article ofcourse but i am bit new in angular kindly explain me how i can work with below scnenario:
    this is modal pop which render the mvc razor partial view
    @{Html.RenderPartial("UploadFiles");}
    with in the page this page
    div id="RCConetent" class="row" ng-app="Reg" ng-controller="RequestCondition">

    @* Today's Stats*@
    طلب للحصول
    '@ViewBag.ScreenTitle'

    @*A fully responsive premium quality admin template built on Twitter Bootstrap by EGrappler.com. These are some dummy lines to fill the area.
    851

    423

    922

    25%

    *@
    1
    أهلا وسهلا!
    2
    حدد مزرعة
    3
    شروط وإجراءات الحصول على الخدمات
    @*
    4
    إجراء الخدمة
    *@
    5
    ارسل طلب
    @{Html.RenderPartial("UploadFiles");}

    @**@
    شروط الخدمة
    S.No.
    وصف
    الفئة المسؤولة
    إرفاق المستندات
    الحالة
    @*
    *@
    @*
    Row
    First Name
    Last Name
    Email
    *@
    {{item.ROWNUM}}
    {{item.DESCRIPTION}}
    {{item.CATEGORYDESCRIPTION}}

    Required Attached
    @*
    2
    Peter
    Parker
    peterparker@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    3
    John
    Rambo
    johnrambo@mail.com
    Required
    *@
    @*href="@Url.Action("RegistrationVerification", "Account", new { step = "registration request" })"*@





    with javascript below
    @section Scripts {
    $(document).ready(function () {
    //$('#adeel').chosen();
    function rowStyle(row, index) {
    var classes = ['active', 'success', 'info', 'warning', 'danger'];
    if (index % 2 === 0 && index / 2 < classes.length) {
    return {
    classes: classes[index / 2]
    };
    }
    return {};
    }
    var Reg = angular.module('Reg', [
    'Reg.ctrl.popup'
    ]).config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
    // Specify the three simple routes ('/', '/About', and '/Contact')
    $routeProvider.when('/', {
    templateUrl: '/Shared/UploadFiles',
    controller: 'homeCtrl',
    });
    $routeProvider.otherwise({
    redirectTo: '/'
    });
    // Specify HTML5 mode (using the History APIs) or HashBang syntax.
    $locationProvider.html5Mode(false).hashPrefix('!');
    }]);
    Reg.controller('RequestCondition', function ($scope, $http) {
    $scope.ServiceConditions = [];
    $scope.LoadServiceCondidence = function () {
    $http({
    url: path + "/Service/GetRequestConditions",
    method: "GET"
    }).success(function (data, status, headers, config) {
    // console.log(data);
    angular.copy(data.dataset, $scope.ServiceConditions);
    console.log($scope.ServiceConditions)
    $.each($scope.ServiceConditions, function (key, value) {
    // console.log(value);
    });
    }).error(function (data, status, headers, config) {
    //something went wrong
    //alert('Error getting data');
    });
    };
    $scope.LoadServiceCondidence();
    $scope.ClickUpload = function (data) {
    console.log(data);
    // alert("hi");
    // data.DESCRIPTION = 'aaaaaaaaaaa';
    $("#popUpUploadFiles").css("display", "block");
    //$("#popUpUploadFiles").removeClass('fade');
    data.ISATTACHED = 1;
    };
    $scope.onFileSelect=function(files)
    {
    console.log(files);
    }
    $scope.MoveFarward = function () {
    window.location.href = "@Url.Action("SubmitRequest", "Service" , null)";
    };
    });
    angular.bootstrap(document.getElementById("RCConetent"), ['Reg']);
    });
    when i render the partial view which contains the following js and html
    ×
    Thank you for visiting EGrappler.com
    {{vm.title}}
    Name
    Size (kb)
    Created on
    Modified on
    no photos
    {{photo.Name}}
    {{photo.Size}}
    {{photo.Created | date: short}}
    {{photo.Modified | date: short}}
    Select and upload new photos
    Files:
    {{photo.name}}
    (function() {
    "use strict";
    /**
    * @ngdoc function
    * @name webApiSample.controller:HomeCtrl
    * @description
    * # HomeCtrl
    * Controller of the webApiSample
    */
    angular
    .module("Reg")
    .controller("popup", ["$window",
    "fileService", "Upload", "apiUrl", function($window, fileService, Upload, apiUrl) {
    var vm = this;
    //Variables
    vm.photos = [];
    vm.files = [];
    vm.previewPhoto = {};
    vm.spinner = {
    active: true
    };
    //Functions
    function setPreviewPhoto(photo) {
    vm.previewPhoto = photo;
    }
    function activate() {
    vm.spinner.active = true;
    fileService.getAll()
    .then(function (data) {
    vm.photos = data.Photos;
    vm.spinner.active = false;
    setPreviewPhoto();
    }, function(err) {
    console.log("Error status: " + err.status);
    vm.spinner.active = false;
    });
    }
    function uploadFiles(files) {
    Upload.upload({
    url: apiUrl,
    data: { file: files }
    })
    .then(function(response) {
    activate();
    setPreviewPhoto();
    }, function(err) {
    console.log("Error status: " + err.status);
    vm.spinner.active = false;
    });
    $window.location.reload();
    }
    function removePhoto(photo) {
    fileService.deletePhoto(photo.Name)
    .then(function() {
    activate();
    setPreviewPhoto();
    });
    }
    //Set scope
    activate();
    vm.uploadFiles = uploadFiles;
    vm.remove = removePhoto;
    vm.setPreviewPhoto = setPreviewPhoto;
    }
    ]);
    })();
    it is throwing error that "popup " function is not find.
    my question are how to use the mvc partial control like i have in the page of mvc like i am using with angular js if you see code you will understand my scnerio clearly.
    thanks in advance and pardon for my bad english :)

Recent Posts

8-Step AWS to Microsoft Azure Migration Strategy

Microsoft Azure and Amazon Web Services (AWS) are two of the most popular cloud platforms.…

2 days ago

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

1 week ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

3 weeks ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

1 month ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

1 month ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

2 months ago