This article is part of a series of posts on RequireJs, AnguarJs, Grunt and Bower. Here are the parts so far:
- AngularJs, RequireJs, Grunt and Bower Part1 - Getting Started and concept with ASP.NET MVC.
- AngularJs, RequireJs, Grunt and Bower Part2 - Grunt
- AngularJs, RequireJs, Grunt and Bower Part3 - generator-angular-requirejs-grunt-bower with ASP.NET MVC
- AngularJs, RequireJs, Grunt and Bower Part4 - How to extend your own code with ASP.NET MVC
- 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
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
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
Reference
- Making RequireJS play nice with ASP.NET MVC by Yngve Bakken Nilsen
- RequireJS API config
- Bower Package manager
- 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.