Hi,
Angular 2 is a very promising framework and one of its biggest advantages is that it takes the power of Typescript and hence, increases the front end productivity and quality of code.
The Angular 2 team introduced in the recent releases, the modularity with the âNgModuleâ decorators which adds another very exciting feature that allows to structure complex applications very efficiently.
One of the trickiest things for Angular 2 newcomers it to make the first things done ! The first âHello Worldâ application is in itself challenging as it requires knowledge about many other tools and libraries.
The Project Features
In this post, I share with you a starter project using Visual Studio 2015. The project is characterized by:
- The application is an ASP.NET CORE web application.
- Using the latest release of Angular 2
- Using SASS as a CSS pre-processor
- Using two A2 modules (module 1) and (module 2)
- The booting applications is composed of two components: component 1 from module 1 and component 2 from module 2
- Component 2 uses service 2 from module 2
- Component 1 uses service 1 from module 1 and service 2 from module 2
- The component templates are in separate HTML files
- The component css files are in separate css files.
- The project includes one MVC controller that uses a razor layout page.
- The project supports multi-environment configurations. For production, the application uses minified js and css files.
When running, the applications looks like the following snapshot:
The resulting page will aggregate two components coming from two separate modules âModule 1â and âModule 2â.
Naming Conventions
For the purpose of an efficient code understandability and communication, we will adopt the following naming convention:
- Typescript classes are named using the UpperCamelCase convention.
- The typescript component files are suffixed with â.component.tsâ. For example, the component âMyButtonComponentâ will be in the file âmy-button.component.tsâ.
- Component classes will be suffixed with âComponentâ. For example, âMyButtonComponentâ.
- The typescript services files are suffixed with â.service.tsâ. For example the service âDataServiceâ will be in the file âdata.service.tsâ.
- Angular 2 services classes are suffixed with âServiceâ. For example, âDataServiceâ.
- The typescript module files are suffixed with â.module.tsâ. For example the service âAdminModuleâ will be in the file âadmin.module.tsâ.
- Angular 2 module classes are suffixed with âModuleâ. For example, âAdminModuleâ.
The Project Structure
To achieve the features described in the previous section, we need a structured project composed of many files and directories.

The structure is composed of the following elements:
- wwwroot: the root hosting folder. All the static files (css, js) will be copied to this folder.
- css: the folder that will contain css files.
- lib: css lib files (for example bootstrap files)
- app: contains application css (normal and minified) files.
- components: contains the components CSS files. The css files are organized by A2 modules. For example, the subfolder XXX contains the CSS of the module XXX components.
- scripts: the folder that will contain JavaScript files
- lib: contains the lib files (for example angular 2, zonejs or systemjs files).
- app: the application js source code. Most of the files are created by compiling the typescript files located in the âClientDevâ directory.
- dev: user js files.
- dist: user minified files.
- templates: contains the components HTML templates files. The files are organized by A2 modules. For example, the subfolder XXX contains the HTML template files of the module XXX components.
- ClientDev: contains the client development files such as Typescript and Sass files.
- HTML: this subfolder contains the component HTML templates files organized by modules. Keeping the HTML templates initially here permits a safe cleaning of the wwwroot templates subfolder.
- Scripts: contains the typescript files.
- main.ts: the Angular2 bootstrap file.
- XXX: each module has its own subfolder under the âScriptsâ folder.
- xxx.module.ts: the typescript module file.
- Components: this subfolder contains the components of the module âXXXâ;
- Services: this subfolder contains the services of the module âXXXâ;
- Style: contains the application sass files that will be compiled and injected into the wwwroot/css/app folder.
- components: this subfolder contains the individual sass files of the A2 components.
- Controllers: the ASP.NET core controllers
- Views: the ASP.NET razor files.
The Gulp Tasks
To achieve the A2 integration, it is necessary to configure the adequate gulp tasks (in gulpfile.js) that allow mostly to move and compile source files from the ClientDev folder (ts, html and sass) to the wwwroot folder (js, html and css).
Gulp Declarations
The first thing to configure are the module and the global variables that are used by gulp. This is illustrated by the snippet below:
1: var gulp = require('gulp');
2: var merge = require('merge-stream');
3: var clean = require('gulp-clean');
4: var sass = require('gulp-sass');
5: var cssmin = require('gulp-cssmin');
6: var rename = require('gulp-rename');
7: var jsmin = require('gulp-jsmin');
8:
9: var webroot = "./wwwroot/";
10: var clientDevRoot = "./ClientDev/";
11:
12:
13:
14: var paths = {
15: css: {
16: sassSource: clientDevRoot + "Style/**/*.scss",
17: appDestination: webroot + "css/app",
18: libDestination: webroot + "css/lib",
19: fontsDestination: webroot + "css/fonts"
20: },
21: js:
22: {
23: tsSource: clientDevRoot + "Scripts/**/*.ts",
24: jsSource: clientDevRoot + "Scripts/**/*.js",
25: mapSource: clientDevRoot + "Scripts/**/*.map",
26: appDestination: webroot + "scripts/app",
27: libDestination: webroot + "scripts/lib"
28: },
29: html: {
30: source: clientDevRoot + "HTML/**/*.html",
31: destination: webroot
32: }
33: };
Lines 1 to 7 declare the gulp modules to be used. The merge module is used to merge multiple tasks into one. The clean module is used to cleaning. The sass module is used to compile sass files. The cssmin and jsmin are used to css and js minification. Finally, the rename module is used to rename files.
The paths global variable indicates the source files included in the ClientDev directory and the result files that will be copied to the wwwroot directory.
The âangular_libâ task
This job copies the angular distribution files from the ânode_modulesâ folder to âwwwroot/scripts/libâ.
1: gulp.task('angular_lib', function (done) {
2: var taskRx = gulp.src([
3: 'node_modules/@angular/**/bundles/*',
4: ]).pipe(gulp.dest(paths.js.libDestination + "/@angular"));
5: })
The âother_libâ task.
This task is about copying the other JS library files such as RxJs.
1: /* other js libraries */
2: gulp.task('other_lib', function (done) {
3: var task1 = gulp.src([
4: 'node_modules/core-js/client/shim.min.js',
5: 'node_modules/core-js/client/shim.js',
6: 'node_modules/zone.js/dist/zone.js',
7: 'node_modules/zone.js/dist/zone.min.js',
8: 'node_modules/reflect-metadata/Reflect.js',
9: 'node_modules/reflect-metadata/Reflect.js.map',
10: 'node_modules/systemjs/dist/system.src.js',
11: 'node_modules/systemjs/dist/system.js',
12: 'node_modules/systemjs/dist/system.js.map'
13: ]).pipe(gulp.dest(paths.js.libDestination));
14: var task2 = gulp.src([
15: 'node_modules/rxjs/**/*.js',
16: ]).pipe(gulp.dest(paths.js.libDestination + '/rxjs'));
17: var task4 = gulp.src([
18: 'node_modules/rxjs/**/*.map',
19: ]).pipe(gulp.dest(paths.js.libDestination + '/rxjs'));
20: var task3 = gulp.src([
21: 'node_modules/foundation/js/vendor/*.js',
22: ]).pipe(gulp.dest(paths.js.libDestination));
23: c
24: return merge(task1, task2, task3, task4);
25: });
The âlib_jsâ task
This task is a combination of the two previous tasks consisting in copying all the JavaScript library files.
gulp.task('lib_js', ['other_lib', 'angular_lib']);
The âapp_htmlâ task
This task consists in copying the HTML template files (and eventually some static html files) from ClientDev to wwwroot.
1: gulp.task('app_html', function () {
2: return gulp.src(paths.html.source)
3: .pipe(gulp.dest(paths.html.destination));
4:
5: });
The âapp_cssâ task
This tasks is related to the compilation of sass files and the generation of css and minified css files that are copied to wwwroot/css/app.
1: gulp.task('app_css', function () {
2: return gulp.src(paths.css.sassSource)
3: .pipe(sass().on('error', sass.logError))
4: .pipe(gulp.dest(paths.css.appDestination))
5: .pipe(rename({ suffix: '.min' }))
6: .pipe(cssmin())
7: .pipe(gulp.dest(paths.css.appDestination));
8:
9: });
The âapp_jsâ task
This tasks copies the js files generated from the typescript files located in ClientDev/Scripts folder to the wwwroot/scripts/app/dev. The minified version is copied to wwwroot/scripts/app/dist.
1: gulp.task('app_js', function (done) {
2: var task1 = gulp.src([
3: paths.js.jsSource
4: ]).pipe(gulp.dest(paths.js.appDestination + '/dev'))
5: .pipe(jsmin())
6: .pipe(gulp.dest(paths.js.appDestination + '/dist'));
7: var task2 = gulp.src([
8: paths.js.mapSource
9: ]).pipe(gulp.dest(paths.js.appDestination + '/dev'))
10: .pipe(gulp.dest(paths.js.appDestination + '/dist'));
11:
12: return merge(task1, task2);
13: });
The âapp_angularâ task.
Finally, this tasks performs all the angular staff: moving HTML templates, css files and JavaScript files. It is a combination of all the app_xxx tasks.
gulp.task('app_angular', ['app_js', 'app_html', 'app_css']);
Key Source Files
Startup.cs
The main tasks is to configure the web application. Line 24 specifies that we are using MVC and configure the default routing for our application.
1: public class Startup
2: {
3: // This method gets called by the runtime. Use this method to add services to the container.
4: // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
5: public void ConfigureServices(IServiceCollection services)
6: {
7: services.AddMvc();
8: }
9:
10: // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
11: public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
12: {
13: if (env.IsDevelopment())
14: {
15: app.UseDeveloperExceptionPage();
16: }
17: else
18: {
19: app.UseExceptionHandler("/Home/Error");
20: }
21:
22: app.UseStaticFiles();
23:
24: app.UseMvc(routes =>
25: {
26: routes.MapRoute(
27: name: "default",
28: template: "{controller=Home}/{action=Index}/{id?}");
29:
30: routes.MapRoute(
31: name: "spa-fallback",
32: template: "{*url}",
33: defaults: new { controller = "Admin", action = "Index" });
34:
35: });
36:
37:
38: }
_Layout.cshtml
This file is located in the âViews/Sharedâ folder and is the default layout for our views. As we target an SPA application, we wonât have a huge number of views as they will be represented by A2 HTML templates. Please notice (line 9 to 34) that we are using the multi-environment capabilities of the new ASP.NET core. For example, in a production environment, we will use the scripts located in the scripts/app/dist instead of those located in scripts/app/dev.
1: <!DOCTYPE html>
2: <html>
3: <head>
4: <base href="/Admin">
5: <meta charset="utf-8" />
6: <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7: <title>A2 Starter</title>
8:
9: <environment names="Development">
10:
11: <link href="~/css/app/site.css" rel="stylesheet" />
12: <script src="/scripts/lib/shim.js"></script>
13: <script src="~/scripts/lib/zone.js"></script>
14: <script src="/scripts/lib/Reflect.js"></script>
15: <script src="/scripts/lib/system.src.js"></script>
16: <script src="~/scripts/app/dev/Config/systemjs.config.js"></script>
17: <script>
18: System.import('app/dev/main')
19: .then(null, console.error.bind(console));
20:
21: </script>
22: </environment>
23: <environment names="Staging,Production">
24: <link href="~/css/app/site.min.css" rel="stylesheet" />
25: <script src="/scripts/lib/shim.min.js"></script>
26: <script src="~/scripts/lib/zone.min.js"></script>
27: <script src="/scripts/lib/Reflect.js"></script>
28: <script src="/scripts/lib/system.js"></script>
29: <script src="~/scripts/app/dist/Config/systemjs.config.js"></script>
30: <script>
31: System.import('app/dist/main')
32: .then(null, console.error.bind(console));
33: </script>
34: </environment>
35: </head>
36: <body>
37:
38:
39:
40: <div>
41: @RenderBody()
42: </div>
43:
44:
45: @RenderSection("scripts", required: false)
46: </body>
47: </html>
main.ts
This file is located in the ClientDev folder root. It consists in bootstrapping our A2 application by using the âAppComponentâ that belongs to âModule1â.
1: /// <reference path="../../typings/index.d.ts"/>
2:
3: import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4: import { Module1Module } from './Module1/module1.module';
5: platformBrowserDynamic().bootstrapModule(Module1Module);
module1.module.ts
This file is located in ClientDev/Module1. Its is the module declaration file for the module âModule 1â. Notice that our starting point is the âAppComponentâ (line 11). Notice also that we are using components and services from âModule 2â by using the âimportsâ directive (line 9).
1: import { NgModule } from "@angular/core";
2: import { BrowserModule } from "@angular/platform-browser";
3: import { AppComponent } from "./Components/app.component";
4: import { Component1Component } from "./Components/component1.component";
5: import { Service1 } from "./Services/service1.service";
6: import { Module2Module } from "./../Module2/module2.module";
7:
8: @NgModule({
9: imports: [BrowserModule, Module2Module],
10: declarations: [AppComponent, Component1Component],
11: bootstrap: [AppComponent],
12: providers: [Service1Service]
13: })
14: export class Module1Module { }
The App Component âAppComponentâ
This is the bootstrapping component. Its only role is to aggregate the two other components âComponent 1â and âComponent 2â.
The typescript code of âAppComponentâ is in ClientDev/Module1/Components/app.component.ts:
1: import { Component } from "@angular/core";
2: @Component({
3: selector: "starter-app",
4: templateUrl: "/templates/module1/app.html",
5: styleUrls: ["css/app/components/module1/app.css"]
6: })
7:
8: export class AppComponent {
9: constructor() {
10:
11: }
12: }
The âAppComponentâ HTML template is located in ClientDev/HTML/templates/app.html:
1: <starter-comp1>
2: </starter-comp1>
3: <starter-comp2>
4: </starter-comp2>
The Component âComponent 1â.
This component belongs to âModule 1â. It is worth to point out that this component uses two services (Service1 and Service2) that come from t The typescript file is located in ClientDev/Module1/Components/component1.component.ts. Notice that the component uses its own css file (line 8) that will be generated by compiling sass files contained in ClientDev/Style/components/module1/component1.sass.
1: import { Component, OnInit } from "@angular/core";
2: import { Service1Service } from "./../Services/service1.service";
3: import { Service2Service } from "./../../Module2/Services/service2.service";
4:
5: @Component({
6: selector: "starter-comp1",
7: templateUrl: "templates/module1/component1.html",
8: styleUrls: ["css/app/components/module1/component1.css"]
9:
10:
11: })
12: export class Component1Component implements OnInit {
13: text: string;
14: constructor(private _s1: Service1Service, private _s2: Service2Service) { }
15:
16: ngOnInit() {
17: this.text = `${this._s1.getText()} ${this._s2.getText()}`;
18: }
19: }
The HTML template file of this component is pretty simple :
<h2>Component From Module 1</h2>
<h2>{{text}}</h2>
The Service âService1Serviceâ
This is a very simple service that is injectable to components by using the âInjectableâ decorator. This service is located in ClientDev/Module1/Services/service1.service.ts.
1: import { Injectable } from "@angular/core";
2:
3: @Injectable()
4: export class Service1Service {
5: getText(): string {
6: return "text from service 1";
7: }
8: }
The Module âModule 2â
This is a separate module that contains its own components and modules. The module declaration file is located in ClientDev/Scripts/Module2/module2.module.ts. By using the âexportsâ directive (line 11), we declare that the component âComponent2Componentsâ can be used by other modules.
1: import { NgModule } from "@angular/core";
2: import { BrowserModule } from "@angular/platform-browser";
3:
4: import { Component2Component } from "./Components/component2.component";
5: import { Service2Service } from "./Services/service2.service";
6:
7:
8: @NgModule({
9: imports: [BrowserModule],
10: declarations: [Component2Component],
11: exports: [Component2Component],
12: providers: [Service2Service]
13: })
14: export class Module2Module { }
The âComponent2â Component and âService2â Service
They are similar to âComponent 1â and âService 2â except they are located in another âModuleâ.
The Source Code
The source code is available here. Enjoy !
The Bottom Line
The purpose of this article is to propose an ideal structure that handles complex angular projects and takes into account development and production environments. Our structure is based on the modularity principle of A2 and is supported by gulp tasks that handles the most important every-day tasks of a front-end developer.