Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MVVM with MVC-Razor and AngularJS Global Controllers

0.00/5 (No votes)
2 Feb 2015 1  
Using angularjs global controllers and MVC Razor to divide page (load) rendering between server and browser

Introduction

In this tip, I am going to explain how to build ASP MVC SPA using AngularJs global controllers.

Last few years, we have seen the emergence of plenty of JavaScript frameworks for building client side SPA and probably the most known is AngularJs .The main concept behind it is to load all application files to the browser and run it once in the client side using an API server side to load data. This is the opposite of what we call traditional web applications that renders entirely (most part) on the server side. This design is not new, I would say SilverLight, Flash JavaApplets..... had done the same. And I'll not tell about their success. What changed is now we use JavaScript instead of C#, JAVA, Actionscript.

Those framework are touted (evengelised) using a small demo application called TodoMVC and running this application, all things run well and we can see the magic of data binding ,DI, productivity and ....

But when you develop a large application mainly an enterprise application using those frameworks, your smiles and enthusiasm will not be the same.

Here below, I will list some problems I encountered while implementing large web applications using AngularJs and some critics that I would like to expose to the community.

  • Loading all JS files to the browser is not very wise
  • Rendering all pages on client side is not an ideal solution: many users of the application don't have extras machines and processors. A lot of them run 2Go RAM or less machines (not all internet users work in Google Inc). I have seen AngularJs application leaking memory and not rendering on small machines.
  • Security: All JS are visible on the browser (they say they are mimified and linted and...) but still are accessible for every one and every one can see the code logic and exploit it for an attack.
  • All application routes all exposed in JS file. Every one authorized or not can see all the routes
  • Caching everything: Some JS files or HTML files contain sensitive data but some people cache everything they say to enhance performance but what about security
  • HTTP calls: Doing with those framework SPAs you have not to count the client/server round trip ! And talking performance the client/server call is expensive.
  • Lot of web controls are JQuery based controls so, porting them into those frameworks is not so peaceful
  • Controlling different environments (legacy browsers, mobile, desktop...) is done on the client side

If I summarize, I would like to say that I faced a lot of limitations working with the new JS frameworks, mainly security, performance and productivity .

Background

By this contribution, I would like to resolve some difficulties I faced during the implementation of AngularJs SPA:

  • Divide the page load: combine the server side rendering and client side rendering
  • Reduce the Client/server round trips: the server will send all that the client needs at once
  • Security: Send to the client only what it needs
  • Load only what the client needs
  • Control different environments on the server

For the last point, some people proposed using RequireJS to load asynchronously but this will only defer the problem because RequireJS loads JS files but doesn't remove them.

In this, I will use ASP.NET MVC with Razor and AngularJS with global controllers.

AngularJS globals Controllers work like this we can inject a controller directly in the view, some are like this:

HTML
<script>
function thispageCtrl($scope)
{
  $scope.message="hello global";
}
</script>
<div ng-controller="thispageCtrl">
    <p>{{message}}</p>
</div> 

In Angular app, if we have any route that points this page, it will render normally.

In AngularJs 1.3.... we must specify this in app config and add a single line like this:

JavaScript
//
.config(['$controllerProvider', function ($controllerProvider) {
    $controllerProvider.allowGlobals();
  }])
//

Using the Code

I attached with this tip a running Visual Studio ASP MCV application.

Image 1

The App_JS folder contains the Angular application in the root we have config.js file where we configure simple user routes and config-admin.js to configure admin routes.

config.js file looks like this:

JavaScript
//
(function (win, ng) {

'use strict';
  ng.module('app')
   .constant('routes', [
{
            url: '/',
            config: {
              templateUrl: 'home/home',
              settings: {
              }
            }
          }, {
            url: '/contact',
            config: {
              templateUrl: 'home/contact',
              settings: {
              }
            }
          }, {
            url: '/about',
            config: {
              templateUrl: 'home/about',
              settings: {
              }
            }
          }, {
            url: '/Employee',
            config: {
              templateUrl: 'Employee/index',
              settings: {
              }
            }
          }  , { url: '/Employee/details/:id',
            config: {
              templateUrl: function (params) {
                return 'Employee/details/' + params.id;
              },
              settings: {
              }
            }
          } 
   ]);
})(this, angular)

//

Another Js file of the App_Js folder is the traditional App.js that looks like this:

JavaScript
//
 (function (window, ng) {
var app = ng.module('app', [
       'ngAnimate',        // animations
         'ngRoute',          // routing
         'ngSanitize',       // sanitizes html bindings (ex: sidebar.js)
  ])
.config(['$routeProvider', 'routes', function ($routeProvider, routes) {
    routes.forEach(function (r) {
      $routeProvider.when(r.url, r.config);
    });
    $routeProvider.otherwise({ redirectTo: '/' });
  }])
.config(['$controllerProvider', function ($controllerProvider) {
    $controllerProvider.allowGlobals();
  }])
 .config(['$provide', function ($provide) {
      $provide.decorator('$exceptionHandler',
          ['$delegate', 'logger', function ($delegate, logger) {
            return function (exception, cause) {
              $delegate(exception, cause);
              var errorData = { message: exception, cause: cause };
              logger.error(errorData)
            };
          }]);
}]).config(['$httpProvider', function ($httpProvider) {
    $httpProvider.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
  }])
})(this, angular)

//

in the other side 

On the other side, we can see the page rendering with Razor and AngularJS.

This is just an example of rendering the EmployeeList.

JavaScript
//
@using razorAngular.Resources 
@model razorAngular.Models.EmployeeListViewModel
@{
    Layout = null;
}
<script>
    function employeeListVM($scope,$rootScope) {
        $rootScope.title="@Messages.Employees";
        $scope.vieModel= @Html.Raw(Html.ToJson(Model));
        $scope.sortby = function (item) {
            return item[$scope.orderBy];
        }
    }
</script>

<div ng-controller="employeeListVM">
 <h2>@Messages.Employees</h2>
    <p><strong>@Messages.Total : </strong>{{vieModel.total}}</p>
    <table class="table table-hover">
        <thead>
            <tr>
                <th ng-repeat="header in vieModel.headers">
        <a href="javascript:;" 
        ng-click="$parent.orderBy=$index+1">{{header}}</a></th>
                <th>Details</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="employee in vieModel.data| orderBy:sortby track by $index ">
                <td ng-repeat="item in employee.slice(1) track by $index">{{item}}</td>
                <td>
                    <a href="#/Employee/details/{{employee[0]}}" 
            class="glyphicon glyphicon-eye-open">Details</a>
                    <a href="#/Employee/details/{{employee[0]}}" 
            class="glyphicon glyphicon-edit">Edit</a>
                </td>
            </tr>
        </tbody>
    </table>
</div>

//

As is visible in the above code, we can see the Razor view model converted to JSON and then send to the browser.

Points of Interest

  • Divide the page rendering between the server and the client
  • Only one client/server Http trip to render a page
  • All sensitive data is controlled server side

Hope this will be useful!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here