Angular Folder Structure¶
Based on best practices from the community, other github Angular projects, developer experience from production Angular projects, and contributors, this project goal is to create a skeleton structure which is flexible for projects big or small.
This project defines directory structure in an Angular application and it is a working application. The code for this project can be found at https://github.com/mathisGarberg/angular-folder-structure
Directory Tree Structure¶
This is a default Angular application directory tree:
.
├── e2e
│ └── src
└── src
├── app
├── assets
└── environments
This tree represents the directories this repository proposes be added to a default Angular application:
.
├── media
└── src
├── app
│ ├── core
│ ├── data
│ ├── layout
│ ├── modules
│ └── shared
└── styles
Each part of this tree is optional so please read through the Directory Structure Parts carefully to understand them all.
Structure Overview¶
The angular-folder-structure project goal is to create a skeleton structure which is flexible for projects big or small.
The Angular style guide has this to say on the subject of directory structure:
Have a near-term view of implementation and a long-term vision. Start small but keep in mind where the app is heading down the road.
All of the app’s code goes in a folder named src. All feature areas are in their own folder, with their own NgModule.
While such instructions are nice to hear they don’t give real-world skeleton exprience. But we’ve taken this advice to heart to build our skeleton and document all the parts in line with the official documentation.
Creating an Application¶
Start your project using the ng
command:
ng new
You will be prompted for the project name. Create a name in lower case with
dashes between the words like album-collection-organizer
Next you will be asked to add Angular Routing. We recommend you select Yes (which is not the default).
Next you will be asked to select the Style Sheet Format. We recommend you select SCSS
ng
will now create a default skeleton applicaiton and install your vendors.
Adding Structure¶
The rest of this documentaion covers new structure which is built on top of
the ng
generated skeleton. Every part of angular-folder-structure
is
optional so we suggest you review each part in this documentation to see if it
is appropriate for your project.
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Media Directory¶
The media
directory is used to store supporting files for the application.
Things like requiremenets docs, text outlines, etc. This is the junk drawer
for the project.
Install¶
mkdir media
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Core Module¶
This module is for classes used by app.module. Resources which are always loaded such as route guards, HTTP interceptors, and application level services, such as the ThemeService ang logging belong in this directory.
Note
This module is recommended for a path alias to @app
Install¶
ng generate module Core
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Data Module¶
The data module is a top level directory and holds the schema (models/entities) and services (repositories) for data consumed by the application.
By default there are two subdirectories:
~/src/app/data
/schema
/service
The schema directory holds the class definition files for data structures. An example data structure:
export class Project {
link: string;
title: string;
thumbnail: string;
}
The service directory holds the services for fetching data. The service files are not necessarily a 1:1 match with schema files. An example service file:
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Project } from '../schema/project';
import { ApiService } from './api.service';
const routes = {
projects: '/projects',
project: (id: number) => `/projects/${id}`
};
@Injectable({
providedIn: 'root'
})
export class ProjectService {
constructor(
private apiService: ApiService) {}
getAll(): Observable<Array<Project>> {
return this.apiService.get(routes.projects);
}
getSingle(id: number): Observable<Project> {
return this.apiService.get(routes.project(id));
}
}
Multiple Data Sources¶
If your application consumes data from more than one source then the data directory should be restructured to contain subdirectories for each data source. Do not create multiple modules for each data source:
~/src/app/data
/data-source-one
/schema
/service
/data-source-two
/schema
/service
/data.module.ts
Schema Naming Standard¶
A schema file is very much like an entity file in an Object Relational Mapper. This schema file is central to your application’s consumption of data and therefore does not need cursory decorators such as calling it ProjectSchema or ProjectModel. Schemas are special because they are the only plain-named class in the application.
Install¶
ng generate module Data
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Layout Directory¶
The layout directory is a container of components which are declared in the AppModule. The directory contains page-level components of content such as a common footer, navigation, and header. It also contains page layouts for the different sections of your application.
Components like footer and navigation are handled the Angular way by importing them into a template:
<app-nav></app-nav>
Layouts are handled in a rather clever way. By using child routes a top level
route can define a layout to be used for its children. Each module has its own
routing so the top level AppRoutingModule
includes the module as a child of
a route. This code block is taken from app-routing.module.ts
and trimmed
of extra content:
{
path: '',
component: ContentLayoutComponent,
children: [
{
path: 'dashboard',
loadChildren: () =>
import('./modules/home/home.module').then(m => m.HomeModule)
}
]
}
When a route is called at /dashboard
the ContentLayoutComponent
is used
as a layout and handling of the routing is handed off to the HomeModule.
The ContentLayoutComponent
has a router-outlet
:
<div [class]="theme">
<div class="mat-app-background">
<app-nav></app-nav>
<div class="container">
<router-outlet></router-outlet>
</div>
<app-footer (changeTheme)="onThemeChange($event)"></app-footer>
</div>
</div>
This router-outlet
is used to display a route and component defined in the
routing of the HomeRoutingModule
:
So the routes are /dashboard
and /dashboard/projects/:id
and they use
the ContentLayoutComponent for their layout.
Install¶
mkdir src/app/layout
ng generate component layout/Header
ng generate component layout/Footer
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Module Directory¶
The modules directory contains a collection of modules which are each independent of each other. This allows Angular to load only the module it requires to display the request thereby saving bandwidth and speeding the entire application.
In order to accomplish this each module must have its own routing which is a
loadChildren
route resource defined in the AppRoutingModule
. This is
also covered in the layout documentation
A route can have children and each child can have a loadChildren property.
From app-routing.module.ts
:
{
path: '',
component: ContentLayoutComponent,
canActivate: [NoAuthGuard], // Should be replaced with actual auth guard
children: [
{
path: 'dashboard',
loadChildren: () =>
import('./modules/home/home.module').then(m => m.HomeModule)
},
{
path: 'about',
loadChildren: () =>
import('./modules/about/about.module').then(m => m.AboutModule)
},
{
path: 'contact',
loadChildren: () =>
import('./modules/contact/contact.module').then(m => m.ContactModule)
}
]
},
Each child must have its own base path from which it can load children from a
module in the modules
directory. Here is the routing for the About page:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AboutComponent } from './pages/about/about.component';
const routes: Routes = [
{
path: '',
component: AboutComponent
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AboutRoutingModule { }
It is necessary to add the child routes to the RouterModule
through forChild
.
Besides routing any module inside the modules
directory can be as simple or
complicated as you wish.
Install¶
mkdir src/app/modules
For each new module run ng generate module modules/NewModule
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Styles Directory¶
The ~/src/styles
directory is used to store scss style sheets for the
application. It can contain themes, Bootstrap, Angular Material, and any
other styles.
~/src/styles.scss
is installed in the default Angular skeleton. It should
contain @import statments for all your global application scss files.
For instance it can import theme files stored in the ~/src/styles
directory.
Themes¶
The ~/src/styles/themes
directory should contain the application-wide themes. This application
includes two theme-files, black-theme.scss and light-theme.scss.
A theme file generates the color-palette that composes the final theme and is constructed of three main palettes: primary, accent and warn. These palettes are defined using the mat-palette mixin, which accepts a mat-color and a hue-number that represents different shades of the chosen color. In terms of code, this is what we have:
$my-black-primary: mat-palette($mat-grey, 700, 300, 900);
$my-black-accent: mat-palette($mat-blue-grey, 400);
$my-black-warn: mat-palette($mat-red, 500);
$my-black-theme: mat-dark-theme(
$my-black-primary,
$my-black-accent,
$my-black-warn
);
The themes are included in the styles.scss file along with the mat-core mixin, which adds the base styles to material components.
@import '~@angular/material/theming';
@import './styles/themes/black-theme.scss';
@import './styles/themes/light-theme.scss';
@include mat-core();
.my-light-theme {
@include angular-material-theme($my-light-theme);
}
.my-dark-theme {
@include angular-material-theme($my-dark-theme);
}
The downside here is that the approach above only will style material components and not custom ones.
To achieve this, we’ve added a file called project-container.component.scss-theme.scss
. This file
imports the material theme and defines a mixin that styles the content with the appropriate color values
- pulling color-palettes from the theme.
@import '~@angular/material/theming';
@mixin my-project-container-component-theme($theme) {
$accent: map-get($theme, accent);
.active {
color: mat-color($accent, default-contrast);
background-color: mat-color($accent);
&:hover {
color: mat-color($accent, default-contrast);
background-color: mat-color($accent);
}
}
}
Then those files are referred to in the styles.scss files:
@import 'app/modules/home/page/project-item/project-container.component.scss-theme.scss';
@mixin custom-components-theme($theme) {
// ...
@include my-project-container-component-theme($theme);
}
.my-light-theme {
// ...
@include custom-components-theme($my-light-theme);
}
.my-dark-theme {
// ...
@include custom-components-theme($my-black-theme);
}
The application content needs to be placed inside either a mat-sidenav-container
element
or have the mat-app-background
class applied to work. This application follows the last
approach by appending this class to the div
that wraps the app-content in the
src/app/layout/content-layout/content-layout.component.html
file:
<div class="my-dark-theme">
<div class="mat-app-background">
<app-nav></app-nav>
<div class="container">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
</div>
</div>
The height of the viewport the theme should affects is also defined:
.mat-app-background {
height: 100%;
}
Bootstrap¶
The ~/src/styles
directory can be used for compiling bootstrap and storing
other scss resources. To install a custom bootstrap download the source files,
extract bootstrap into ~/src/styles/bootstrap
, then modify the
bootstrap/scss/_variables.scss
. Include boostrap in the styles.scss
:
@import './styles/bootstrap/scss/bootstrap.scss';
Install¶
mkdir src/styles
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Default Directory Structure¶
This is the directory structure this repository recommends. It is listed here in one place as a reference.
About¶
Inspired by the original blog post, this structure uses all the directory structure parts and is a strait-forward installation.
Tree Structure¶
.
├── e2e
│ └── src
├── media
└── src
├── app
│ ├── core (@app)
│ ├── data
│ ├── layout
│ ├── modules
│ └── shared (@shared)
├── assets
├── environments (@env) [@env links to environment file]
└── styles
└── themes
Install¶
These instructions are to install this directory structure to a brand new
ng
created application:
mkdir media
ng generate module Core
ng generate module Shared
ng generate module Data
mkdir src/app/layout
mkdir src/app/modules
mkdir src/styles && mkdir src/styles/themes
json --version || npm install -g json
json -f tsconfig.json -I -c "this.compilerOptions.paths = {}"
json -f tsconfig.json -I -e "this.compilerOptions.paths['@app/*'] = ['app/core/*']" \
-e "this.compilerOptions.paths['@shared/*'] = ['app/shared/*']" \
-e "this.compilerOptions.paths['@env/*'] = ['environment/*']"
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Neolithic Directory Structure¶
– Tom H Anderson <tom.h.anderson@gmail.com>
This is an alternative to the primary directory structure this repository promotes.
About¶
This directory structure corrects ng
creation of all files inside the
app
directory. The default ng
structure forces all code to exist as a
subdirectory to app
without giving a clear space for files which should
exist beneath the app
dir.
This directory structure moves ~/src/app
to ~/src/modules/app
then
creates a symlink from ~/src
to ~/src/app
(not shown and hidden from
vscode) so ng
will still place new files where they belong. By moving
app
to a subdirectory of the modules
it clears space for files which
truly belong under the app
module. This removes the requirement of a
core
module.
All modules exist in the same directory. This removes the special handling
for the app
module and flattens the modules.
Tree Structure¶
.
├── e2e
│ └── src
├── media
└── src
├── assets
├── environments (@env) [@env links to environment file]
├── modules
│ ├── app (@app)
│ │ └── layout
│ ├── data (@data)
│ └── shared (@shared)
└── styles
└── themes
Install¶
These instructions are to install this directory structure to a brand new
ng
created application:
mkdir media
mkdir src/module
mkdir src/styles && mkdir src/styles/themes
mv src/app src/module
mkdir src/module/app/layout
cd src && ln -s . app & cd .
sed -i .bak 's/.\/app/.\/module\/app/g' src/main.ts
ng generate module module/Shared
ng generate module module/Data
json --version || npm install -g json
json -f tsconfig.json -I -c "this.compilerOptions.paths = {}"
json -f tsconfig.json -I -e "this.compilerOptions.paths['@app/*'] = ['app/module/app/*']" \
-e "this.compilerOptions.paths['@shared/*'] = ['app/module/shared/*']" \
-e "this.compilerOptions.paths['@env/*'] = ['environment/*']" \
-e "this.compilerOptions.paths['@data/*'] = ['app/module/data/*']"
mkdir -p .vscode
test -f .vscode/settings.json || echo "{}" > .vscode/settings.json
json -f .vscode/settings.json -I -e "this['files.exclude'] = {'**src/app': true}"
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Path Alias¶
Path aliases simplify paths by giving a link to the path rather than using the the fully qualified path name. Using them will make your code easier to read and maintain.
Create Aliases¶
Run this command to install the json program which we will use to modify
tsconfig.json
:
npm install -g json
To create an alias run this command from your root application directory:
json -f tsconfig.json -I -e "this.compilerOptions.paths['@app/*'] = ['app/core/*']"
The @app
is the alias. The path, app/core/*
in this example, is the
path from the root src
directory to the directory you would like to alias.
Recommended Aliases¶
Recommended are aliases to core, shared,
modules and environment
. These aliases will
simplify your development:
json -f tsconfig.json -I -e "this.compilerOptions.paths['@app/*'] = ['app/core/*']"
json -f tsconfig.json -I -e "this.compilerOptions.paths['@shared/*'] = ['app/shared/*']"
json -f tsconfig.json -I -e "this.compilerOptions.paths['@modules/*'] = ['app/modules/*']"
json -f tsconfig.json -I -e "this.compilerOptions.paths['@env'] = ['environments/environment']"
Note the alias for @env goes directly to the environment file.
Using Aliases¶
When you have aliases defined such as @shared
you can shortcut your use
statements by using the alias:
import { SharedModule } from '../../shared/shared.module';
becomes:
import { SharedModule } from '@shared/shared.module';
The environment @env
alias to a file is used this way:
import { environment } from '@env';
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Furthur Reading¶
- Read the original blog post which started this module: How to define a highly scalable folder structure for your Angular project
- Path Aliasing is a popular way to make your import statements more tidy and is used in this demonstration application.
- Some Best Practices which follow the advice here closely.
Alternative Directory Structure Projects¶
These projects have similar goals to this project and understanding them will give a wider view of directory structure: the problem and solutions.
- angular-starter
- This is a very popular starter kit with lots of tools built in. It is designed to be used when starting a new application. Common directory parts include ~/src/styles.
- $ngx ROCKET
- Extensible Angular 8+ enterprise-grade project generator based on angular-cli with best practices from the community. Includes PWA, Cordova & Electron support, coding guides and more!
- angular-ngrx-material-starter
- Angular, NgRx, Angular CLI & Angular Material Starter Project
- Angular-Full-Stack
- Angular Full Stack project built using Angular 2+, Express, Mongoose and Node. Whole stack in TypeScript.
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Demonstration Application¶
See this application running on our demonstation application
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Install Locally¶
You can install this application locally and run it. Assuming you already have
typescript
, npm
, and ng
installed, clone this repository, cd to the
directory and run npm install
:
git clone https://github.com/mathisGarberg/angular-folder-structure.git
cd angular-folder-structure
npm install
Included with this package are some custom npm scripts. Here is a list of npm run commands and their descriptions:
npm start -> Run dev. server on http://localhost:4200/
npm run build -> Lint code and build app for production in dist folder
npm run test -> Run unit tests via Karma in watch mode
npm test:ci -> Lint code and run unit tests once for continuous integration
npm run e2e -> Run e2e tests using Protractor
npm run lint -> Lint code
Run Locally¶
To run the application type ng serve
then browse to
http://localhost:4200/
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.
Contributing¶
We welcome contributions of all kinds to this repository. Submit a PR or create an issue for anything you can help us improve.
For documentation contributions we recommend you install restructuredText into your copy of vscode so you can lint and preview your work before it is submitted.
This is documentation for angular-folder-structure. If you find this useful please add your ★ star to the project.