In this article we are going to explore together how Angular apps work behind the scenes. At first, we will understand various workspace and application configuration files and finally we will piece together all the files how they are involved in bootstraping.
Angular Project Structure
Before understanding how Angular works under the hood, it’s necessary to understand the workspace structure and application configuration involved in the process. Every Angular apps works in the context of workspace. By default ng new app_name
command generates a skeleton application at root of workspace in below fashion -
workspace/(folder name is app_name given at cmd)
... (workspace-wide config files)
src/ --(source and support files for application)
The above structure is usually referred as ‘multi-repo’ development style where each application has it’s own workspace, ‘workspace’ folder consists of workspace specific configuration files where as ‘src’ folder consists of application specific files and folders.
Workspace configuration files
- angular.json - It specifies workspace-wide and project specific defaults for build and development. For instance, You can have ’n’ number of npm packages installed in your workspace, but you can specify which packages you want in your application in angular.json
- package.json - It consists metadata of project and is used for managing the project’s dependencies, scripts, npm package versions and many more.
- package-lock.json - It provides information of all npm packages installed in npm_modules folder such as package version, hash to verify the package integrity and list of its dependencies.
- tsconfig.json - Since Angular apps are written using typescript, this file specifies root files and compiler options required to compile our application.
Application configuration files
- index.html - This is our main HTML page which is rendered and is displayed to user in browser.
- main.ts - This is responsible for compiling all components and template files with JIT(Just In Time) compiler. We can also use AOT(Ahead Of Time) compiler by adding –aot flag to
ng build
orng serve
CLI commands(Recommended for production environment).
For more detailed explanation of all config files go through official Angular Docs
Angular Bootstrapping
Some of you all might have wondered when I used the term ‘Bootstraping’ earlier. In Angular eco-system Bootstraping
is technique of initializing root module and loading root component into index.html file. When we run ng serve
or ng build
multiple things happen in the background. Below steps provide a brief overview of bootstrapping process-
- Compilation of application codes using
tsc
typescript compiler. - Bundling and minification of javascript files by Webpack.
- Deployment and bootstrapping.
- Run JIT compiler for all components, directives and pipes.
- Render index.html in browser.
The only difference between JIT and AOT compiler is that in AOT compiler, compilation takes place during build time rather than at runtime in browser. Let’s go through above steps in bit more detail -
- angular.json consist of multiple configuration properties related build and development. The one’s which are interesting to us are ‘builder’, ‘index’ and ‘main’ properties under ‘build’ as shown below.
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
.......
"index": "src/index.html",
"main": "src/main.ts",
.......
}
When we run ng serve
or ng build
Angular CLI looks at angular.json for these properties. ‘builder’ property is saying to Angular CLI where it can find Architect builders to build Angular apps for browser environment. where as our ‘index’ and ‘main’ properties specifies index.html file to be rendered in browser and main.ts which handles bootstrapping process. Then Angular CLI calls tsc
typescript compiler to transpile all the typescript codes into javascript codes using configuration provided in tsconfig.json
.
- If we look at index.html file of newly generated Angular app, it only has
<app-root>
element inside<body>
tag.<app-root>
is root component template which is injected at runtime. Apart from these there is neither any javascript file or any reference to style links.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>SampleApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.a7a7ed6d783a0950.css" media="print" onload="this.media='all'">
<noscript>
<link rel="stylesheet" href="styles.a7a7ed6d783a0950.css">
</noscript>
</head>
<body>
<app-root></app-root>
<script src="runtime.0298365924d274ea.js" type="module"></script>
<script src="polyfills.daf14d28d6c3636f.js" type="module"></script>
<script src="main.94e04dd2c597738f.js" type="module"></script>
</body>
</html>
After transpiling all the typescript codes, Angular CLI then uses Webpack, a module bundler to minify and bundle all the files which are then added as reference into index.html file. Let’s look at each of these files :
- runtime.js - It contains utility codes used by Webpack to load other files.
- vendor.js - It contains 3rd party library codes which are imported in our ngModules.
- polyfill.js - It contains polyfills required to make our app compatible among different browsers.
- main.js - It contains all the javascript codes written in our application.
When we run ng serve
application codes are built at local environment and served from memory, where as ng build
outputs all the transpiled codes into dist folder from where it can be deployed to any hosting vendor and served from there.
- At runtime in browser, main.js is responsible for bootstraping our Angular apps. It contains all our application codes and how to compile and run it. We earlier learnt Angular CLI looks at angular.json to find main.ts file. If we look at main.ts file it consist couple of import statements along with some lines of code.
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
Apart from import statements, we are having code to enable production mode if target environment is production but the line which is interesting to us is platformBrowserDynamic().bootstrapModule(AppModule);
. Though it’s single line, lot of action takes place when executed by Angular CLI.
- At first, We are importing
platformBrowserDynamic
module from@angular/platform-browser-dynamic
library. - Then we are calling
platformBrowserDynamic().bootstrapModule(AppModule)
by passing our root AppModule as parameter. - Internally
bootstrapModule
creates an instance of compilerJIT compiler
, where it crawls through all components, directives and pipes declared in AppModule @NgModule decorator and other feature modules imported in AppModule. - Once it finds Component having ‘app-root’ as its selector, it will render the component template in index.html.
But how does Angular know where to find template for <app-root>
in index.html? Let’s find out in next section.
- From the root AppModule we passed earlier, it refers @ngModule decorator in AppModule. @NgModule decorator contains metadata of all the components, directives and pipes under it along with other feature modules imported under AppModule. It also specifies services which are available at app-level under providers and bootstrap property specifying the root component as shown below.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Once it finds the root AppComponent, it then refers @Component decorator in AppComponent. @Component contains metadata of below properties :
- selector : It is used to identify all the components in Angular app.
- templateUrl : It provides path to component HTML template file.
- styleUrls : It provides path to component styles.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'SampleApp';
}
There it is! app-root
specified in selector matches with app-root
in index.html. It then finds HTML template along with styles and renders the same in index.html where app-root
is present.
Conclusion
Though above explanation is not complete, I hope it gave you brief idea on what’s happening under the hood when we run Angular app. Stay tuned for more such interesting articles!