My studying notebook

2015/02/26

[notebook] Google Push Notification API

2/26/2015 10:06:00 AM Posted by Unknown , , , , 80 comments

Google Push Notification

The Google APIs provides push notification that let you watch for changes to resources

Google Drive Push Notification

Google APIs provides Push Notification

You can visit Google APIs Explorer - search watch. There are few APIs support push notification now. Not all of them.
calendar.settings.watchCalendar API v3Watch for changes to Settings resources.
calendar.acl.watchCalendar API v3Watch for changes to ACL resources.
calendar.calendarList.watchCalendar API v3Watch for changes to CalendarList resources.
calendar.events.watchCalendar API v3Watch for changes to Events resources.
storage.objects.watchAllCloud Storage API v1beta2Watch for changes on all objects in a bucket.
reports.activities.watchAdmin Reports API reports_v1Push changes to activities
directory.users.watchAdmin Directory API directory_v1Watch for changes in users list
directory.users.aliases.watchAdmin Directory API directory_v1Watch for changes in user aliases list
storage.objects.watchAllCloud Storage API v1Watch for changes on all objects in a bucket.
youtube.playlistItems.updateYouTube Data API v3Modifies a playlist item. For example, you could update the item’s position in the playlist.
drive.files.watchDrive API v2Subscribe to changes on a file
drive.changes.watchDrive API v2Subscribe to changes for a user.

2014/07/15

AngularJs, RequireJs, Grunt and Bower Part4 - How to extend your own code with ASP.NET MVC

7/15/2014 04:33:00 PM Posted by Unknown , , , , , , , 9 comments

This article is part of a series of posts on RequireJs, AnguarJs, Grunt and Bower. Here are the parts so far:

  1. AngularJs, RequireJs, Grunt and Bower Part1 - Getting Started and concept with ASP.NET MVC
  2. AngularJs, RequireJs, Grunt and Bower Part2 - Grunt
  3. AngularJs, RequireJs, Grunt and Bower Part3 - generator-angular-requirejs-grunt-bower with ASP.NET MVC
  4. AngularJs, RequireJs, Grunt and Bower Part4 - How to extend your own code with ASP.NET MVC
  5. AngularJs, RequireJs, Grunt and Bower Part5 - generator-angular-requirejs-grunt-bower with Express.js //to be done

In previou aticles that we talked about how to integrate AngularJs, Requirejs, Grunt and bower with ASP.NET MVC. If you don’t want to create those file structure manual. You can install generator-angular-requirejs-grunt-bower yeoman generator.

Simple step to install yeoman generator

  • install generator: npm install -g generator-angular-requirejs-grunt-bower
  • cd to project root folder
  • execute command yo angular-requirejs-grunt-bower

Scenario

As basic concept we mentioned that we want to treat ControllerName/actionName as single page application and try to reuse shared code as possible base on this file structure. Now, let’s create a new ASP.NET MVC controller and extend new AngularJs application.

Step 1: create new ASP.NET MVC controller

  1. please create a new controller named ProjectController via Visual studio.
  2. Add new view named Index.cshtml
  3. modify Views/Project/Index.cshtml. The only thing you need to do is add Angular route directive ng-view. We will handle UI in AngularJs partile view.
@{
    ViewBag.Title = "Product";
}

<div ng-view></div>

Step 2: create new AngularJs application

create file structure as following.

  1. Public/js/views/Project, AngularJs application container and named same as Controller
  2. Public/js/views/Project/index.js, application entry point
  3. Public/js/views/Project/patials, we will put all partials view HTML here
  4. Public/js/views/Project/patials/project-index.html, default AngulaJs route view.
  5. Public/js/controllers/product-controller.js, Product controller.
project-index.html: Product partials view
<div>
    this is Project Index page.
</div>
product-controller.js: Product controller
define(['controllers/controllers'], function (controllers) {
    controllers.controller('ProjectIndexCtrl', ['$scope',
        function ($scope) {
            console.log('ProjectIndexCtrl execute.');
        }
    ]);
});

index.js: Product AngularJs single page application entry point.

'use strict';

require([
        'angular',
        'app',
        'domReady',
        'controllers/product-controller',
        'bootstrap'
],
    function (angular, app, domReady) {
        var root = require.toUrl('.').split('.')[0];
        app.config([
            '$routeProvider', '$httpProvider', '$sceDelegateProvider', '$locationProvider',
            function ($routeProvider, $httpProvider, $sceDelegateProvider, $locationProvider) {
                // sec
                $sceDelegateProvider.resourceUrlWhitelist(['self', '.*']);

                // route
                $routeProvider.
                when('/', {
                    templateUrl: '/public/js/views/Product/partials/product-index.html',
                    controller: 'ProjectIndexCtrl',
                    resolve: {}
                }).
                otherwise({
                    redirectTo: '/'
                });
            }
        ]).run([
            '$rootScope',
            function ($rootScope) {
                // global variable

                $rootScope.$safeApply = function ($scope, fn) {
                    $scope = $scope || $rootScope;
                    fn = fn || function () { };
                    if ($scope.$$phase) {
                        fn();
                    } else {
                        $scope.$apply(fn);
                    }
                };
            }
        ]).constant('$', $);

        domReady(function () {
            angular.bootstrap(document.body, ['myAngularApp']);

            $('html').addClass('ng-app: myAngularApp');
        });
    }
);

Preview

Product

Step 3: Let’s extend shared service for different single page application

Till now, we have two AngularJs single page application HomeController and ProductController and they don’t include any AngularJs service, directive or filter module.

  • Add a new ASP.NET MVC BookController ApiController
  • Add a AngularJs server named /Public/js/services/bookService.js. Using ng-source to call API
  • Inject book-service.js to Product application
  • Inject book-service.js to Home application

Add BookController ApiController

// GET: api/Book
public IEnumerable<string> Get()
{
    return new string[] { "ABookApart.CSS3.For.Web.Designers.2010", "AngularJS Directives" };
}

book-service.js

Add a new AngularJs service locate in Public/js/service/book-service.js.

define(['services/services'], function (services) {
    // Book resource
    services.factory('BOOK', ['$resource', function ($resource) {
        var resetUrl = '/api/Book';
        var resource = $resource(resetUrl, {}, { get: { method: 'GET', isArray: true } });

        return resource;
    }]);

    // BookLoader
    services.factory('BookLoader', ['$q', 'BOOK', function ($q, BOOK) {
        return function () {
            var delay = $q.defer();
            BOOK.get(function (data) {
                delay.resolve(data);
            }, function (error) {
                delay.reject('Unable to fetch Book list');
            });
            return delay.promise;
        };
    }]);
});

Inject book-service.js BookLoader to AngularJs route resolve

If you are familier with AngularJs. There is a resolve function you can call in AngularJs route. We define a BookLoader function that implement with promise pattern. Then, we can inject the result to our controller. Resolve can help us to fetch API before controller called.

How can we inject book service to AngularJs application via RequireJs require feature. That’s mean we can inject different feature in other application easily.

js/views/Product/index.js

....
'controllers/product-controller',
'services/book-service',
...

controller/Product-controller.js

controllers.controller('ProjectIndexCtrl', ['$scope', 'books', function ($scope, books) {
    console.log('ProjectIndexCtrl execute.');

    $scope.books = books;
}]);

Preview
preview

Inject book-service.js BOOK service to AngularJs route resolve

We have defined BOOK AngularJs service. Different with Product application. We use BOOK service instead of BooKloader promise service.

// js/views/Home/index.js
...
'domReady',
'controllers/home-controller',
'services/book-service',
'bootstrap'
...
// js/controller/home-controller.js
define(['controllers/controllers'], function (controllers) {
    controllers.controller('HomeCtrl', ['$scope', 'BOOK', function ($scope, BOOK) {
            console.log('HomeCtrl execute.');

            $scope.books = BOOK.get({}, function (books) {
                return books;
            });
    }]);
});

We inject BOOK service in controller directly and bind Book.get() to $scope.books. Whatever we use AngularJs route resolve or inject service directly. They points to same service we defined.

Preview
Preview

Grunt

As we mentioned before. we still can build for every AngularJs we defined. Befere we build our application, we need to modify Gruntfile.js manual.

...
 modules: [{
        name: 'views/Home/index'
    }, {
        name: 'views/Product/index'
    }],
...    
grunt 

Github Source Code

cage1016/AngularJsRequireJsGruntBower

After you clone source code from github repo, you can run following command:

  • git checkout -f step-0 is status project created
  • git checkout -f step-1 is status Nuget install Require.js.
  • git checkout -f step-2 is status project Add AngularJs, RequireJs, Grunt and Bower to Public folder.
  • git checkout -f step-3 is status modify _Layout.chtml to render RequireJs. You will see the final result.
    • Please cd to AngularJsRequireJsGruntBower\AngularJsRequireJsGruntBower\Public and execute bower install to restore those library project included.
  • git checkout -f setp-4 is status ready to do grunt tasks.
    • Please cd to AngularJsRequireJsGruntBower\AngularJsRequireJsGruntBower\Public and execute npm install to restore grunt required libraries.
    • execute command grunt to do grunt tasks.
  • git checkout -f step-5 extend Product application
  • git checkout -f step-6 extend AngularJs service book-service and add BookLoader to route resolve
  • git checkout -f step-7 extend AngularJs service book-service and inject BOOK service to controller
  • git checkout -f step-8 add Product to Gruntfile.js module block

2014/07/14

AngularJs, RequireJs, Grunt and Bower Part3 - generator-angular-requirejs-grunt-bower with ASP.NET MVC (Updatd 2014/7/15)

7/14/2014 04:29:00 PM Posted by Unknown , , , , , , , 10 comments

This article is part of a series of posts on RequireJs, AnguarJs, Grunt and Bower. Here are the parts so far:

  1. AngularJs, RequireJs, Grunt and Bower Part1 - Getting Started and concept with ASP.NET MVC
  2. AngularJs, RequireJs, Grunt and Bower Part2 - Grunt
  3. AngularJs, RequireJs, Grunt and Bower Part3 - generator-angular-requirejs-grunt-bower with ASP.NET MVC
  4. AngularJs, RequireJs, Grunt and Bower Part4 - How to extend your own code with ASP.NET MVC
  5. AngularJs, RequireJs, Grunt and Bower Part5 - generator-angular-requirejs-grunt-bower with Express.js //to be done

We are talking about integrate AngularJs, RequireJs, Grunt and Bower in ASP.NET MVC project in previous part.

Javascript file structure

Public
    └── js
    |   ├── controllers
    |   ├── css
    |   ├── directives
    |   ├── services
    |   ├── vendor
    |   ├── filters
    |   ├── views
    |   |   └── ...
    |   ├── app.js
    |   └── config.js
    └── release
        ├── views
        |   └── ...
        ├── app.js
        └── config.js       

Above is the font-end file structure we mentioned in previous articles. It’s dozens of files we need to create. So, i put those of work to yeoman generator-angular-requirejs-grunt-bower.

Getting started

  • cd to project root
  • Install the generator: npm install -g generator-angular-requirejs-grunt-bower
  • Run: yo angular-requirejs-grunt-bower

Yeoman Generator: angular-requirejs-grunt-bower

Github:
cage1016/generator-angular-requirejs-grunt-bower

2014/07/02

AngularJs, RequireJs, Grunt and Bower Part2 - Grunt (Update: 2014/7/15)

7/02/2014 11:29:00 AM Posted by Unknown , , , , , , , 51 comments

This article is part of a series of posts on RequireJs, AnguarJs, Grunt and Bower. Here are the parts so far:

  1. AngularJs, RequireJs, Grunt and Bower Part1 - Getting Started and concept with ASP.NET MVC
  2. AngularJs, RequireJs, Grunt and Bower Part2 - Grunt
  3. AngularJs, RequireJs, Grunt and Bower Part3 - generator-angular-requirejs-grunt-bower with ASP.NET MVC
  4. AngularJs, RequireJs, Grunt and Bower Part4 - How to extend your own code with ASP.NET MVC
  5. AngularJs, RequireJs, Grunt and Bower Part5 - generator-angular-requirejs-grunt-bower with Express.js //to be done

Grunt

Grunt

Why use a task runner?

In one word: automation. The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes. After you’ve configured it, a task runner can do most of that mundane work for you—and your team—with basically zero effort.

Why use Grunt?

The Grunt ecosystem is huge and it’s growing every day. With literally hundreds of plugins to choose from, you can use Grunt to automate just about anything with a minimum of effort. If someone hasn’t already built what you need, authoring and publishing your own Grunt plugin to npm is a breeze.

If you are familiar with Grunt. You could feel how powerful it is, how could it reduce your repetitive tasks and make your work more easier.

Grunt with our ASP.NET MVC Project

Concept

...
Public
    └── js
    |   ├── controllers
    |   ├── css
    |   ├── directives
    |   ├── services
    |   ├── vendor
    |   ├── filters
    |   ├── views
    |   |   └── ...
    |   ├── app.js
    |   └── config.js
    └── release
        ├── views
        |   └── ...
        ├── app.js
        └── config.js       

RequireJs, AnguarJs, Grunt, Bower overview

In AngularJs, RequireJs, Grunt and Bower Part1 - Getting Started and concept with ASP.NET MVC ~ 竹部落 post. We talk about how to integrate AngularJs, RequireJs, Grunt and Bower with our ASP.NET MVC project and put all of code in Public/js folder. Browser will load those files one by one by following RequireJs definition and dependencies we define in Public/js/config.js.

Be a large AngularJs single page application, you might include a lots of file to the project. For example: in our ASP.NET MVC project. Implement the file structure i mentioned in last post. Browser will load more the 20 file. Web page loading speed will increase if browser load more files. During development, separation of concerns (SoC) will help us to break down the problem to smaller. So, we need a solution to help us to combine related file and decrease web page loding speed in release mode. That’s Rquirejs + Grunt.

RequireJs + Grunt

Our gold is improve page loading speed by reduce include file count in our project in final release/product. RequireJs has an optimization tool can help us to combine related file and minifies them and grunt-contrib-requirejs and put RequireJs work with Grunt well.

REQUIREJS OPTIMIZER

Combines related scripts together into build layers and minifies them via UglifyJS (the default) or Closure Compiler (an option when using Java).
Optimizes CSS by inlining CSS files referenced by @import and removing comments.

Gruntfile.js
module.exports = function(grunt) {
    'use strict';
    require('load-grunt-tasks')(grunt);

    // configurable paths
    var mvcConfig = {
        js: 'js',
        release: 'release',
        tmp: 'tmp'
    };

    grunt.initConfig({
        mvc: mvcConfig,
        clean: {...},
        copy: {...},
        requirejs: {...},
        concat: {...},
        uglify: {...},
        htmlmin: {...},
        jshint: {...},
        cssmin: {...}
    });

    // task
    grunt.registerTask('build', [
        'clean:release',
        'clean:tmp',
        'copy:vendor',
        'copy:module',
        'requirejs',
        'copy:basic',
        'copy:css',
        'cssmin',
        'clean:tmp',
        'htmlmin'
    ]);

    grunt.registerTask('default', [
        'jshint',
        'build'
    ]);
};

Grunt scenario

We can define multiple grunt tasks in Gruntfile.js. All you need to do is executing command grunt. Grunt will execute all of tasks one by one. How, let we check before doing the grunt task.

  • using bower package manager to handle what library we include and put all libraries in vendor. Ex: AngularJs, jQuery , moment ects.
  • config.js define the library dependencies and library path.
  • implement file structure as we mentioned before.

Our Grunt scenario is copy required file from public/js/... to public/tmp/... folder. Grunt task requirejs:complie will combine scripts together and put it in public/release folder.

grunt-contrib-requirejs optimize RequireJS projects using r.js. Most of needed configuration is same as RequireJs required but file path. We need to modify file path to our temporary folder tmp.

 paths: {
        ...
        'angular': '../<%= mvc.tmp %>/vendor/angular',
        'jquery': '../<%= mvc.tmp %>/vendor/jquery.min',
        ...

One more thing. We need to replace file path in our release RequireJs config.js from js to release. It will make sure relese RequireJs config.js is reference to current source folder.

onBuildRead: function(moduleName, path, contents) {
    if (moduleName === 'config') {
        var x = (function(contents) {
            var regex = /'(vendor|libs)[^']*'/gm;
            var matches = contents.match(regex);
            for (var i = 0; i < matches.length; i++) {
                var match = matches[i];
                var m = matches[i].split('/');
                contents = contents.replace(match, '\'vendor/' + m[m.length - 1].toLowerCase());
            }
            return contents;
        })(contents);

        return x.replace(/\/public\/js/g, '/public/release');
    }
    return contents;
},

Visit to Github to check whole Gruntfile.js file.

build.txt

After you execute command grunt. build.txt will be created automatically. It is a requirejs:complie log file and show you a list what libraries and user define scripts are combined together base on Gruntfile.js requirejs complie module setting.

// config.js
requirejs: {
    compile: {
        options: {
            ...
            modules: [{
                name: 'views/Home/index'
            }],
            ...
        }
    }
},
// build.txt
views/Home/index.js
----------------
vendor/jquery.min.js
vendor/moment.min.js
vendor/angular.js
vendor/respond.js
controllers/controllers.js
filters/filters.js
directives/directives.js
vendor/angular-resource.js
services/services.js
vendor/angular-animate.js
vendor/angular-cookies.js
vendor/angular-route.js
vendor/angular-sanitize.js
vendor/angular-touch.js
vendor/bootstrap.min.js
vendor/ui-bootstrap.min.js
vendor/ui-bootstrap-tpls.min.js
app.js
vendor/domReady.js
controllers/home-controller.js
views/Home/index.js

Preview

Final step, we need to change solution configuration from Debug to Release in Visual Studio toolbar and rebuild the project. Open Chrome Developer tools and switch to Network panel. You will see browser load two files config.js and index.js

Release mode

Reference

  1. Grunt: The JavaScript Task Runner
  2. RequireJs

Github Source Code

cage1016/AngularJsRequireJsGruntBower

After you clone source code from github repo, you can run following command:

  • git checkout -f step-0 is status project created
  • git checkout -f step-1 is status Nuget install Require.js.
  • git checkout -f step-2 is status project Add AngularJs, RequireJs, Grunt and Bower to Public folder.
  • git checkout -f step-3 is status modify _Layout.chtml to render RequireJs. You will see the final result.
    • Please cd to AngularJsRequireJsGruntBower\AngularJsRequireJsGruntBower\Public and execute bower install to restore those library project included.
  • git checkout -f setp-4 is status ready to do grunt tasks.
    • Please cd to AngularJsRequireJsGruntBower\AngularJsRequireJsGruntBower\Public and execute npm install to restore grunt required libraries.
    • execute command grunt to do grunt tasks.

2014/06/28

AngularJs, RequireJs, Grunt and Bower Part1 - Getting Started and concept with ASP.NET MVC (update:2014/7/16)

6/28/2014 01:20:00 PM Posted by Unknown , , , , , , , 12 comments

This article is part of a series of posts on RequireJs, AnguarJs, Grunt and Bower. Here are the parts so far:

  1. AngularJs, RequireJs, Grunt and Bower Part1 - Getting Started and concept with ASP.NET MVC.
  2. AngularJs, RequireJs, Grunt and Bower Part2 - Grunt
  3. AngularJs, RequireJs, Grunt and Bower Part3 - generator-angular-requirejs-grunt-bower with ASP.NET MVC
  4. AngularJs, RequireJs, Grunt and Bower Part4 - How to extend your own code with ASP.NET MVC
  5. AngularJs, RequireJs, Grunt and Bower Part5 - Yoeman generator-angular-requirejs-grunt-bower with Express.js //to be done

There are more and more fancy web applications. Developers have one same gold that they want their visitors have better user experience. So, they start working with heave JavaScript. (AngularJs, Backbone.js, Emberjs etc.).

There are dozen of awesome Javascript frameworks now, You can choose the suitable one for your project. In my company, we use ASP.NET MVC to develop web application. From ASP.NET MVC 2, 3, 4 to 5. I am looking a good practice to implement a good Javascript framework to my project till i meet AngularJs. AngularJs Javascript is top 10 Github repo until now and hosted by Google.

It’s very easy to use AngularJs in your web application page.

<!doctype html>
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.13/angular.min.js"></script>
  </head>
  <body>
    <div>
      <label>Name:</label>
      <input type="text" ng-model="yourName" placeholder="Enter a name here">
      <hr>
      <h1>Hello {{yourName}}!</h1>
    </div>
  </body>
</html>

Above is a simple sample code from AngularJs official page. live demo here

MVVM concept and powerful two-way databinding can help you solve the problem you might working hard with jQuery. But for the large web application. Is a better way to integrate AngularJs in your project? YES, you can re-factor your code to module and include it to project.

// i define a ui.tiff module to hanle tiff viewer
angular.module('ui.tiff', ['ui.tiff.controller', 'ui.tiff.service', 'ui.tiff.directive', 'ui.tiff.filter']);
angular.module('ui.tiff.controller', []).controller('_aCtrl', ['$scope',
    function($scope) {
        ...
    }
]);

angular.module('ui.tiff.service', []).factory('ImageInfo', ['$resource',
    function($resource) {
        ...
    }
]);

angular.module('ui.tiff.directive', []).directive('tiffClick', [
    function() {
        ...
    }
]);

angular.module('ui.tiff.filter', []).filter('thumbnail', function() {
    ...
});

Concept

Be an ASP.NET MVC developer. How can we integrate APS.NET MVC with AngulaJs as well? ASP.NET MVC can render view page for each controller method. for example:

  • Views/Home/Index
  • Views/Home/About
  • Views/Management/Index

For Above route URL, we can get different ASP.NET MVC view page. But, i want to treat each of them as individual SPA (single page appliation) and share the same code (AngluarJs server or AngularJs filter modules etc.) for different SPA.

  • Views/Home/Index (SPA)
  • Views/Home/About (SPA)
  • Views/Home/Contact
  • Views/Management/Index (SPA)

Dan Wahlin has many awesome articles talk about ASP.NET MVC with AngularJs

Scenario

Yngve Bakken Nilsen has a awesome article talk about Making RequireJS play nice with ASP.NET MVC

RequireJs

In a heavey Javascript project. you might load more than 10 Javascript libraries easily. One of most important issues is to figure out Javascript library dependency and library synchronous loading. RequireJs is a JavaScript file and module loader.You can define library dependency (not worry which library should be loaded earlier than another one.) and avoid page loading block problem.

My solution is base on Yngve Bakken Nilsen article (RequierJs with ASP.NET MVC) but integrate with AngularJs, Grunt and Bower.

ASP.NET MVC File Structure

...
├── Controller
|   └── HomeController
├── Views
|   └── Home
|       ├── About.cshtml
|       ├── Contact.cshtml
|       └── Index.cshtml
├── Models
...

Above is ASP.NET MVC normal file structure. You can add NameController and assign view template views/ControllerName/ActionName to render view page. As i menetioned before, i’ll like to treat Views/Controler as SPA. so, i rearrange the basic file strcutre and put all of Javscript file in Public folder.

for the controller has more than one action view page. I move those page to front-end AngularJs framework. AngularJs route will handle this part.

AngularJs, RequireJs, Grunt and Bower OverView

RequireJs, AnguarJs, Grunt, Bower overview

Above diagram is whole project overview. RequireJs let us can include specific library we needed. All Javascript codes are put in Public folder.

...
Public
|   └── js
|   |   ├── controllers
|   |   |   ├── controllers.js
|   |   |   └── home-controller.js
|   |   ├── css
|   |   ├── directives
|   |   |   ├── directives.js
|   |   |   └── home-directive.js
|   |   ├── services
|   |   |   └── services.js
|   |   ├── vendor
|   |   ├── filters
|   |   |   ├── filters.js
|   |   |   └── common-filer.js
|   |   ├── views
|   |   |   ├── Management
|   |   |   |   ├── partials
|   |   |   |   |   └── management-index.html
|   |   |   |   └── index.js
|   |   |   └── Home
|   |   |       ├── partials
|   |   |       |   └── home-index.html
|   |   |       └── index.js
|   |   ├── app.js
|   |   └── config.js
|   └── release
Views
    ├── Management
    |   └── Index.cshtml    
    └── Home
        └── Index.cshtml    

As you can see the modified file structure. Public/js/views file structure like ASP.NET MVC Views. So, we could dynamic to load SPA by RequireJs if we have defined it in Public/js/views/mapping to ASP.NET MVC controller.

// Route: http://localhost/Home/Index
require( [ "/public/js/config.js" ], function() {
    require( [ "views/Home/Index" ] ); 
});

// Route: http://localhost/Account/Index
// no any AngularJs application will be loaded.
require( [ "/public/js/config.js" ], function() {
    require( [""] ); 
});

Angularjs Application

AngularJs, RequireJs concept

The basic concept is that i want to treat ASP.NET MVC route Views/ControllerName as a individual AngularJs SPA and Dynamic be loaded if i have defined it.

  • Define AngularJs module using RequireJs define.
  • Include needed module with RequireJs require.

I can define task orientation AngularJs module as you can see in above diagram. It’s easy to include specific controller, service, filter and direcitve that inherent from controllers/controllers, services/services, filters/filters and directives/directives in application. Put all together and bootstrap AngularJs application when DOM ready.

Everything looks so good but RequireJs config.js. I have to tell RequireJs the external libraries path and module name i include to the application. There are more information about RequireJs configuration in here.

require.config({
    baseUrl: "/public/js",
    waitSeconds: 200,
    paths: {
        'angular': 'vendor/angular/angular.min',
        'angular.zh-tw': 'vendor/angular-i18n/angular-locale_zh-tw',
        'angular.route': 'vendor/angular-route/angular-route.min',
        'angular.resource': 'vendor/angular-resource/angular-resource.min',
        'angular.animate': 'vendor/angular-animate/angular-animate.min',
        'angular.sanitize': 'vendor/angular-sanitize/angular-sanitize.min',
        'angular.cookies': 'vendor/angular-cookies/angular-cookies.min',

        'jquery': 'vendor/jquery/jquery.min',
        'moment': 'vendor/momentjs/min/moment.min',
        'respond': 'vendor/respond/dest/respond.src',
        'domReady': 'vendor/requirejs-domready/domReady',

        'bootstrap': 'vendor/bootstrap/dist/js/bootstrap.min',
        'uiBootstrap': 'vendor/angular-ui-bootstrap-bower/ui-bootstrap.min',
        'uiBootstrapTpl': 'vendor/angular-ui-bootstrap-bower/ui-bootstrap-tpls.min',
    },
    shim: {
        'moment': {
            exports: 'moment'
        },
        'angular': {
            deps: ['jquery', 'moment'],
            exports: 'angular'
        },
        'angular.zh-tw': ['angular'],
        'angular.route': ['angular'],
        'angular.resource': ['angular'],
        'angular.sanitize': ['angular'],
        'angular.animate': ['angular'],
        'angular.cookies': ['angular'],
        'respond': {
            exports: 'respond'
        },
        'bootstrap': {
            deps: ['jquery'],
            exports: 'bootstrap'
        },
        'uiBootstrap': {
            deps: ['angular', 'bootstrap'],
            exports: 'uiBootstrap'
        },
        'uiBootstrapTpl': {
            deps: ['angular', 'uiBootstrap']
        }
    },
    //urlArgs: "bust=" + (new Date()).getTime()
    urlArgs: "bust=v8"
});

It’s very convenient to integrate Bower package manager with your project. I put all of external libraries in vendor and save those library in bower.json. You can restore those libaries by execute command bower install

//bower.json
{
  "name": "application name",
  "version": "0.0.1",
  "dependencies": {},
  "devDependencies": {
    "angular": "~1.2.16",
    "requirejs-domready": "~2.0.1",
    "angular-route": "~1.2.16",
    "angular-resource": "~1.2.16",
    "angular-sanitize": "~1.2.16",
    "angular-cookies": "1.2.16",
    "momentjs": "~2.5.1",
    "angular-animate": "~1.2.16",
    "respond": "~1.4.2",
    "jquery": "1.10.2",
    "bootstrap": "~3.1.1",
    "angular-i18n": "~1.2.16",
    "angular-ui-bootstrap-bower": "~0.11.0",
    "moment": "~2.6.0"
  },
  "resolutions": {
    "angular": "~1.2.16"
  }
}

Intergrate with ASP.NET MVC

    ...
    var action = helper.ViewContext.RouteData.Values["action"];
    var controller = helper.ViewContext.RouteData.Values["controller"];
    string module = string.Format("views/{0}/{1}", controller, action);

    string jsLocation = "/public/js/";
    if (File.Exists(helper.ViewContext.HttpContext.Server.MapPath(Path.Combine(jsLocation, module + ".js"))))
    {
        require.AppendLine("require( [ \"" + jsLocation + core + "\" ], function() {");
        require.AppendLine("    require( [ \"" + module + "\"] );");
        require.AppendLine("});");
    }
    else
    {
        require.AppendLine("require( [ \"" + jsLocation + core + "\" ], function() {");
        require.AppendLine("    require( [ \"jquery\", \"bootstrap\" ] );");
        require.AppendLine("});");
    }
    ...

Final step. All you need to do is go Views/Shared/_Layout.cshtml and add @Html.ViewSpecificRequireJS() before body close tag. @Html.ViewSpecificRequireJS() is a ASP.NET MVC static method and it will read your project file structure. If you define an application in /public/js/view/Home and ASP.NET MVC route is mapping to views/controllerName. @Html.ViewSpecificRequireJS() static method will redener it, otherwise not. Don’t forget to include @Html.ViewSpecificRequireJS() Namespace @using Requirejs.Helpers in top of Views/Shared/_Layout.cshtml page.

you have to add <div ng-view></div> to thoes ASP.NET MVC views page for AngularJs route.

Preview

AngularJs, RequireJs concept

AngularJs, RequireJs concept

Reference

  1. Making RequireJS play nice with ASP.NET MVC by Yngve Bakken Nilsen
  2. RequireJS API config
  3. Bower Package manager
  4. Dan Wahlin - Dan Wahlin

Github Source Code

cage1016/AngularJsRequireJsGruntBower

After you clone source code from github repo, you can run following command:

  • git checkout -f step-0 is status project created
  • git checkout -f step-1 is status Nuget install Require.js.
  • git checkout -f step-2 is status project Add AngularJs, RequireJs, Grunt and Bower to Public folder.
  • git checkout -f step-3 is status modify _Layout.chtml to render RequireJs. You will see the final result.
    • Please cd to AngularJsRequireJsGruntBower\AngularJsRequireJsGruntBower\Public and execute bower install to restore those library project included.
  • git checkout -f setp-4 is status ready to do grunt tasks.
    • Please cd to AngularJsRequireJsGruntBower\AngularJsRequireJsGruntBower\Public and execute npm install to restore grunt required libraries.
    • execute command grunt to do grunt tasks.