diff --git a/public/docs/_examples/style-guide/ts/04-14/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/04-14/app/heroes/index.ts
deleted file mode 100644
index a8d7f1d422..0000000000
--- a/public/docs/_examples/style-guide/ts/04-14/app/heroes/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './shared';
-export * from './heroes.component';
diff --git a/public/docs/_examples/style-guide/ts/04-14/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/04-14/app/heroes/shared/hero.model.ts
deleted file mode 100644
index 8f7cc205c8..0000000000
--- a/public/docs/_examples/style-guide/ts/04-14/app/heroes/shared/hero.model.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-// #docregion
-export class Hero {
- id: number;
- name: string;
-}
diff --git a/public/docs/_examples/style-guide/ts/04-14/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/04-14/app/heroes/shared/index.ts
deleted file mode 100644
index 0dceb684c4..0000000000
--- a/public/docs/_examples/style-guide/ts/04-14/app/heroes/shared/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './hero.model';
diff --git a/public/docs/_examples/style-guide/ts/04-14/app/index.ts b/public/docs/_examples/style-guide/ts/04-14/app/index.ts
deleted file mode 100644
index 251d78ac56..0000000000
--- a/public/docs/_examples/style-guide/ts/04-14/app/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './heroes';
-export * from './shared';
-export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/04-14/app/shared/index.ts b/public/docs/_examples/style-guide/ts/04-14/app/shared/index.ts
deleted file mode 100644
index 6820e22fc7..0000000000
--- a/public/docs/_examples/style-guide/ts/04-14/app/shared/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './logger.service';
diff --git a/public/docs/_examples/style-guide/ts/04-14/app/shared/logger.service.ts b/public/docs/_examples/style-guide/ts/04-14/app/shared/logger.service.ts
deleted file mode 100644
index b1f8c7ff21..0000000000
--- a/public/docs/_examples/style-guide/ts/04-14/app/shared/logger.service.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// #docregion
-import { Injectable } from '@angular/core';
-
-@Injectable()
-export class Logger {
-
- constructor() { }
-
-}
diff --git a/public/docs/_examples/style-guide/ts/06-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/06-03/app/app.component.ts
index 5f4ea8dce5..0d0a7d107b 100644
--- a/public/docs/_examples/style-guide/ts/06-03/app/app.component.ts
+++ b/public/docs/_examples/style-guide/ts/06-03/app/app.component.ts
@@ -2,6 +2,8 @@ import { Component } from '@angular/core';
@Component({
selector: 'sg-app',
- template: ''
+ template: `
+
+ `
})
export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/06-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/06-03/app/app.module.ts
index 8677138eef..b19f3fdc00 100644
--- a/public/docs/_examples/style-guide/ts/06-03/app/app.module.ts
+++ b/public/docs/_examples/style-guide/ts/06-03/app/app.module.ts
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
-import { ValidatorDirective } from './shared';
+import { ValidatorDirective, Validator2Directive } from './shared';
@NgModule({
imports: [
@@ -10,7 +10,7 @@ import { ValidatorDirective } from './shared';
],
declarations: [
AppComponent,
- ValidatorDirective
+ ValidatorDirective, Validator2Directive
],
exports: [ AppComponent ]
})
diff --git a/public/docs/_examples/style-guide/ts/06-03/app/shared/index.ts b/public/docs/_examples/style-guide/ts/06-03/app/shared/index.ts
index 2168f8b2c6..ba25e4c458 100644
--- a/public/docs/_examples/style-guide/ts/06-03/app/shared/index.ts
+++ b/public/docs/_examples/style-guide/ts/06-03/app/shared/index.ts
@@ -1 +1,2 @@
export * from './validator.directive';
+export * from './validator2.directive';
diff --git a/public/docs/_examples/style-guide/ts/06-03/app/shared/validator.directive.avoid.ts b/public/docs/_examples/style-guide/ts/06-03/app/shared/validator.directive.avoid.ts
deleted file mode 100644
index c9b724dc53..0000000000
--- a/public/docs/_examples/style-guide/ts/06-03/app/shared/validator.directive.avoid.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// #docregion
-import { Directive, HostBinding, HostListener } from '@angular/core';
-
-// #docregion example
-/* avoid */
-
-@Directive({
- selector: '[tohValidator]',
- host: {
- '(mouseenter)': 'onMouseEnter()',
- 'attr.role': 'button'
- }
-})
-export class ValidatorDirective {
- role = 'button';
- onMouseEnter() {
- // do work
- }
-}
-// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/06-03/app/shared/validator.directive.ts b/public/docs/_examples/style-guide/ts/06-03/app/shared/validator.directive.ts
index 4271da0ef6..d9e32c017f 100644
--- a/public/docs/_examples/style-guide/ts/06-03/app/shared/validator.directive.ts
+++ b/public/docs/_examples/style-guide/ts/06-03/app/shared/validator.directive.ts
@@ -1,7 +1,6 @@
// #docregion
import { Directive, HostBinding, HostListener } from '@angular/core';
-// #docregion example
@Directive({
selector: '[tohValidator]'
})
@@ -11,4 +10,3 @@ export class ValidatorDirective {
// do work
}
}
-// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/06-03/app/shared/validator2.directive.ts b/public/docs/_examples/style-guide/ts/06-03/app/shared/validator2.directive.ts
new file mode 100644
index 0000000000..7936a83cb1
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/06-03/app/shared/validator2.directive.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { Directive } from '@angular/core';
+
+@Directive({
+ selector: '[tohValidator2]',
+ host: {
+ 'attr.role': 'button',
+ '(mouseenter)': 'onMouseEnter()'
+ }
+})
+export class Validator2Directive {
+ role = 'button';
+ onMouseEnter() {
+ // do work
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/app/app.routes.ts b/public/docs/_examples/style-guide/ts/app/app.routes.ts
index 1fb26f08ea..bce6b4df06 100644
--- a/public/docs/_examples/style-guide/ts/app/app.routes.ts
+++ b/public/docs/_examples/style-guide/ts/app/app.routes.ts
@@ -1,58 +1,8 @@
import { Routes } from '@angular/router';
import { AppComponent as S0101 } from '../01-01/app';
-// import { AppComponent as S0207 } from '../02-07/app';
-// import { AppComponent as S0208 } from '../02-08/app';
-// import { AppComponent as S0301 } from '../03-01/app';
-// import { AppComponent as S0302 } from '../03-02/app';
-// import { AppComponent as S0303 } from '../03-03/app';
-// import { AppComponent as S0304 } from '../03-04/app';
-// import { AppComponent as S0305 } from '../03-05/app';
-// import { AppComponent as S0306 } from '../03-06/app';
-// import { AppComponent as S0410 } from '../04-10/app';
-// import { AppComponent as S0414 } from '../04-14/app';
-// import { AppComponent as S0502 } from '../05-02/app';
-// import { AppComponent as S0503 } from '../05-03/app';
-// import { AppComponent as S0504 } from '../05-04/app';
-// import { AppComponent as S0512 } from '../05-12/app';
-// import { AppComponent as S0513 } from '../05-13/app';
-// import { AppComponent as S0514 } from '../05-14/app';
-// import { AppComponent as S0515 } from '../05-15/app';
-// import { AppComponent as S0516 } from '../05-16/app';
-// import { AppComponent as S0517 } from '../05-17/app';
-// import { AppComponent as S0601 } from '../06-01/app';
-// import { AppComponent as S0603 } from '../06-03/app';
-// import { AppComponent as S0701 } from '../07-01/app';
-// import { AppComponent as S0703 } from '../07-03/app';
-// import { AppComponent as S0704 } from '../07-04/app';
-// import { AppComponent as S0901 } from '../09-01/app';
export const routes: Routes = [
{ path: '', redirectTo: '/01-01', pathMatch: 'full' },
{ path: '01-01', component: S0101 },
- // { path: '02-07', component: S0207 },
- // { path: '02-08', component: S0208 },
- // { path: '03-01', component: S0301 },
- // { path: '03-02', component: S0302 },
- // { path: '03-03', component: S0303 },
- // { path: '03-04', component: S0304 },
- // { path: '03-05', component: S0305 },
- // { path: '03-06', component: S0306 },
- ///////////////////{ path: '04-10', component: S0410 },
- // { path: '04-14', component: S0414 },
- // { path: '05-02', component: S0502 },
- // { path: '05-03', component: S0503 },
- // { path: '05-04', component: S0504 },
- // { path: '05-12', component: S0512 },
- // { path: '05-13', component: S0513 },
- // { path: '05-14', component: S0514 },
- // { path: '05-15', component: S0515 },
- // { path: '05-16', component: S0516 },
- // { path: '05-17', component: S0517 },
- // { path: '06-01', component: S0601 },
- // { path: '06-03', component: S0603 },
- // { path: '07-01', component: S0701 },
- // { path: '07-03', component: S0703 },
- // { path: '07-04', component: S0704 },
- // { path: '09-01', component: S0901 },
];
diff --git a/public/docs/_examples/style-guide/ts/app/main.ts b/public/docs/_examples/style-guide/ts/app/main.ts
index 792f873167..eef534bd4d 100644
--- a/public/docs/_examples/style-guide/ts/app/main.ts
+++ b/public/docs/_examples/style-guide/ts/app/main.ts
@@ -16,6 +16,7 @@ import { HeroData } from './hero-data';
import { AppComponent } from './app.component';
import * as s0101 from '../01-01/app/app.module';
+import * as s0205 from '../02-05/app/app.module';
import * as s0207 from '../02-07/app/app.module';
import * as s0208 from '../02-08/app/app.module';
import * as s0301 from '../03-01/app/app.module';
@@ -23,8 +24,10 @@ import * as s0302 from '../03-02/app/app.module';
import * as s0303 from '../03-03/app/app.module';
import * as s0304 from '../03-04/app/app.module';
import * as s0306 from '../03-06/app/app.module';
+import * as s0408 from '../04-08/app/app.module';
import * as s0410 from '../04-10/app/app.module';
-import * as s0414 from '../04-14/app/app.module';
+import * as s0411 from '../04-11/app/app.module';
+import * as s0412 from '../04-12/app/app.module';
import * as s0502 from '../05-02/app/app.module';
import * as s0503 from '../05-03/app/app.module';
import * as s0504 from '../05-04/app/app.module';
@@ -49,6 +52,7 @@ const moduleMetadata = {
InMemoryWebApiModule.forRoot(HeroData),
s0101.AppModule,
+ s0205.AppModule,
s0207.AppModule,
s0208.AppModule,
s0301.AppModule,
@@ -56,8 +60,10 @@ const moduleMetadata = {
s0303.AppModule,
s0304.AppModule,
s0306.AppModule,
+ s0408.AppModule,
s0410.AppModule,
- s0414.AppModule,
+ s0411.AppModule,
+ s0412.AppModule,
s0502.AppModule,
s0503.AppModule,
s0504.AppModule,
@@ -74,7 +80,6 @@ const moduleMetadata = {
s0704.AppModule,
s0901.AppModule,
-
RouterModule.forRoot([
{ path: '', redirectTo: '/01-01', pathMatch: 'full' }
], {/* enableTracing: true */}),
diff --git a/public/docs/_examples/style-guide/ts/systemjs.custom.js b/public/docs/_examples/style-guide/ts/systemjs.custom.js
index bc5a4d2eb3..8181979346 100644
--- a/public/docs/_examples/style-guide/ts/systemjs.custom.js
+++ b/public/docs/_examples/style-guide/ts/systemjs.custom.js
@@ -2,19 +2,23 @@
// extra local packages
var packageNames = [
'01-01', '01-01/app', '01-01/app/heroes', '01-01/app/heroes/shared',
+ '02-05', '02-05/app',
'02-07', '02-07/app', '02-07/app/heroes', '02-07/app/users',
'02-08', '02-08/app', '02-08/app/shared',
- '03-01', '03-01/app', '03-01/app/shared',
- '03-02', '03-02/app', '03-02/app/shared',
- '03-03', '03-03/app', '03-03/app/shared',
- '03-04', '03-04/app', '03-04/app/shared',
- '03-05', '03-05/app', '03-05/app/shared', '03-05/app/shared/spinner', '03-05/app/shared/toast',
+ '03-01', '03-01/app', '03-01/app/core',
+ '03-02', '03-02/app', '03-02/app/core',
+ '03-03', '03-03/app', '03-03/app/core',
+ '03-04', '03-04/app', '03-04/app/core',
+ '03-05', '03-05/app', '03-05/app/core', '03-05/app/core/spinner', '03-05/app/core/toast',
'03-05/app/heroes', '03-05/app/heroes/shared',
- '03-06', '03-06/app', '03-06/app/shared', '03-06/app/shared/spinner', '03-06/app/shared/toast',
+ '03-06', '03-06/app', '03-06/app/core', '03-06/app/core/spinner', '03-06/app/core/toast',
'03-06/app/heroes', '03-06/app/heroes/shared',
+ '04-08', '04-08/app', '04-08/app/heroes',
'04-10', '04-10/app', '04-10/app/shared', '04-10/app/heroes', '04-10/app/shared/spinner', '04-10/app/shared/toast',
- '04-10/app/shared/filter-text', '04-10/app/shared/modal', '04-10/app/shared/nav',
- '04-14', '04-14/app', '04-14/app/heroes', '04-14/app/heroes/shared', '04-14/app/shared',
+ '04-10/app/shared/filter-text',
+ '04-11', '04-11/app', '04-11/app/core', '04-11/app/heroes', '04-11/app/core/spinner',
+ '04-11/app/core/nav',
+ '04-12', '04-12/app', '04-12/app/core', '04-12/app/heroes', '04-12/app/core/nav',
'05-02', '05-02/app', '05-02/app/heroes', '05-02/app/heroes/shared', '05-02/app/heroes/shared/hero-button',
'05-03', '05-03/app', '05-03/app/heroes', '05-03/app/heroes/shared', '05-03/app/heroes/shared/hero-button',
'05-04', '05-04/app', '05-04/app/heroes', '05-04/app/heroes/shared',
diff --git a/public/docs/ts/latest/guide/style-guide.jade b/public/docs/ts/latest/guide/style-guide.jade
index b62f43ed80..db7dd3831f 100644
--- a/public/docs/ts/latest/guide/style-guide.jade
+++ b/public/docs/ts/latest/guide/style-guide.jade
@@ -1,18 +1,13 @@
include ../_util-fns
-.alert.is-important
- :marked
- We are still preparing style recommendations for the new NgModules feature
- introduced in RC5 and will add it to the style guide soon.
-
:marked
Welcome to the Angular Style Guide
## Purpose
- If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular applications, then step right in.
-
- The purpose of this style guide is to provide guidance on building Angular applications by showing the conventions we use and, more importantly, why we choose them.
+ Looking for an opinionated guide to Angular syntax, conventions, and application structure?
+ Step right in!
+ This style guide presents our preferred conventions and, as importantly, explains why.
.l-main-section
:marked
@@ -27,7 +22,7 @@ include ../_util-fns
**Do** is one that should always be followed.
_Always_ might be a bit too strong of a word.
Guidelines that literally should always be followed are extremely rare.
- On the other hand, we need a really unusual case for breaking a *Do* guideline.
+ On the other hand, you need a really unusual case for breaking a *Do* guideline.
.s-rule.consider
:marked
@@ -36,7 +31,7 @@ include ../_util-fns
.s-rule.avoid
:marked
- **Avoid** indicates something we should almost never do. Code examples to *avoid* have an unmistakeable red header.
+ **Avoid** indicates something you should almost never do. Code examples to *avoid* have an unmistakeable red header.
.l-main-section
:marked
@@ -55,7 +50,7 @@ a(id='toc')
1. [Single Responsibility](#single-responsibility)
1. [Naming](#naming)
1. [Coding Conventions](#coding-conventions)
- 1. [Application Structure](#application-structure)
+ 1. [App Structure and Angular Modules](#app-structure-and-angular-modules)
1. [Components](#components)
1. [Directives](#directives)
1. [Services](#services)
@@ -67,7 +62,8 @@ a(id='toc')
:marked
## Single Responsibility
- We apply the [Single Responsibility Principle](https://wikipedia.org/wiki/Single_responsibility_principle) to all Components, Services, and other symbols we create. This helps make our app cleaner, easier to read and maintain, and more testable.
+ Apply the [Single Responsibility Principle](https://wikipedia.org/wiki/Single_responsibility_principle) to all components, services, and other symbols.
+ This helps make the app cleaner, easier to read and maintain, and more testable.
### Rule of One
#### Style 01-01
@@ -180,7 +176,7 @@ a(href="#toc") Back to top
.s-why
:marked
- **Why?** The naming conventions should simply help us find our code faster and make it easier to understand.
+ **Why?** The naming conventions should simply help find desited code faster and make it easier to understand.
.s-why.s-why-last
:marked
@@ -207,7 +203,7 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** use conventional type names including `.service`, `.component`, `.pipe`.
+ **Do** use conventional type names including `.service`, `.component`, `.pipe`, `.module`, `.directive`.
Invent additional type names if you must but take care not to create too many.
.s-why
@@ -231,7 +227,7 @@ a(href="#toc") Back to top
.l-main-section
:marked
- ### Components and Directives
+ ### Symbols and File Names
#### Style 02-03
.s-rule.do
@@ -240,23 +236,24 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** use upper camel case for symbols. Match the name of the symbol to the naming of the file.
+ **Do** use upper camel case for class names. Match the name of the symbol to the name of the file.
.s-rule.do
:marked
- **Do** append the symbol name with the suffix that it represents.
+ **Do** append the symbol name with the conventional suffix for a thing of that type
+ (e.g., `Component`, `Directive`, `Module`, `Pipe`, `Service`).
+.s-rule.do
+ :marked
+ **Do** use give the filename the conventional suffix for a file of that type
+ (e.g., `.component.ts`, `.directive.ts`, `.module.ts`, `.pipe.ts`, `.service.ts`).
.s-why
:marked
**Why?** Provides a consistent way to quickly identify and reference assets.
-.s-why
- :marked
- **Why?** Upper camel case is conventional for identifying objects that can be instantiated using a constructor.
-
.s-why.s-why-last
:marked
- **Why?** The `Component` suffix is more commonly used and is more explicitly descriptive.
+ **Why?** Upper camel case is conventional for identifying objects that can be instantiated using a constructor.
- var top="vertical-align:top"
table(width="100%")
@@ -269,7 +266,7 @@ table(width="100%")
td
code-example.
@Component({ ... })
- export class AppComponent {}
+ export class AppComponent { }
td
:marked
app.component.ts
@@ -277,7 +274,7 @@ table(width="100%")
td
code-example.
@Component({ ... })
- export class HeroesComponent
+ export class HeroesComponent { }
td
:marked
heroes.component.ts
@@ -285,7 +282,7 @@ table(width="100%")
td
code-example.
@Component({ ... })
- export class HeroListComponent
+ export class HeroListComponent { }
td
:marked
hero-list.component.ts
@@ -293,7 +290,7 @@ table(width="100%")
td
code-example.
@Component({ ... })
- export class HeroDetailComponent
+ export class HeroDetailComponent { }
td
:marked
hero-detail.component.ts
@@ -301,10 +298,34 @@ table(width="100%")
td
code-example.
@Directive({ ... })
- export class ValidationDirective
+ export class ValidationDirective { }
td
:marked
validation.directive.ts
+ tr(style=top)
+ td
+ code-example.
+ @NgModule({ ... })
+ export class AppModule
+ td
+ :marked
+ app.module.ts
+ tr(style=top)
+ td
+ code-example.
+ @Pipe({ name: 'initCaps' })
+ export class InitCapsPipe implements PipeTransform { }
+ td
+ :marked
+ init-caps.pipe.ts
+ tr(style=top)
+ td
+ code-example.
+ @Injectable()
+ export class UserProfileService { }
+ td
+ :marked
+ user-profile.service.ts
:marked
a(href="#toc") Back to top
@@ -349,7 +370,7 @@ table(width="100%")
td
code-example.
@Injectable()
- export class HeroDataService {}
+ export class HeroDataService { }
td
:marked
hero-data.service.ts
@@ -357,7 +378,7 @@ table(width="100%")
td
code-example.
@Injectable()
- export class CreditService {}
+ export class CreditService { }
td
:marked
credit.service.ts
@@ -365,7 +386,7 @@ table(width="100%")
td
code-example.
@Injectable()
- export class Logger {}
+ export class Logger { }
td
:marked
logger.service.ts
@@ -382,9 +403,13 @@ a(href="#toc") Back to top
:marked
**Do** put bootstrapping and platform logic for the app in a file named `main.ts`.
+.s-rule.do
+ :marked
+ **Do** include error handling the bootstrapping logic.
+
.s-rule.avoid
:marked
- **Avoid** putting app logic in the `main.ts`. Instead consider placing it in a Component or Service.
+ **Avoid** putting app logic in the `main.ts`. Instead consider placing it in a component or service.
.s-why
:marked
@@ -394,6 +419,9 @@ a(href="#toc") Back to top
:marked
**Why?** Follows a familiar convention from other technology platforms.
++makeExample('style-guide/ts/02-05/main.ts', '', 'main.ts')
+:marked
+
a(href="#toc") Back to top
.l-main-section
@@ -403,7 +431,7 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** Use lower camel case for naming the selectors of our directives.
+ **Do** Use lower camel case for naming the selectors of directives.
.s-why
:marked
@@ -422,7 +450,13 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** use a custom prefix for the selector of our components. For example, the prefix `toh` represents from **T**our **o**f **H**eroes and the prefix `admin` represents an admin feature area.
+ **Do** use a hyphenated, lowercase element selector value (e.g. `admin-users`).
+
+
+.s-rule.do
+ :marked
+ **Do** use a custom prefix for a component selector.
+ For example, the prefix `toh` represents from **T**our **o**f **H**eroes and the prefix `admin` represents an admin feature area.
.s-rule.do
:marked
@@ -430,15 +464,15 @@ a(href="#toc") Back to top
.s-why
:marked
- **Why?** Prevents name collisions.
+ **Why?** Prevents element name collisions with components in other apps and with native HTML elements.
.s-why
:marked
- **Why?** Makes it easier to promote and share our feature in other apps.
+ **Why?** Makes it easier to promote and share the component in other apps.
.s-why.s-why-last
:marked
- **Why?** Our Components and elements are easily identified.
+ **Why?** Components are easy to identify in the DOM.
+makeExample('style-guide/ts/02-07/app/heroes/hero.component.avoid.ts', 'example', 'app/heroes/hero.component.ts')(avoid=1)
:marked
@@ -458,7 +492,11 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** use a custom prefix for the selector of our directives (for instance below we use the prefix `toh` from **T**our **o**f **H**eroes).
+ **Do** use a custom prefix for the selector of directives (e.g, the prefix `toh` from **T**our **o**f **H**eroes).
+
+.s-rule.do
+ :marked
+ **Do** spell non-element selectors in lower camel case unless the selector is meant to match a native HTML attribute.
.s-why
:marked
@@ -466,7 +504,7 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** Our Directives are easily identified.
+ **Why?** Directives are easily identified.
+makeExample('style-guide/ts/02-08/app/shared/validate.directive.avoid.ts', 'example', 'app/shared/validate.directive.ts')(avoid=1)
:marked
@@ -619,6 +657,98 @@ table(width="100%")
a(href="#toc") Back to top
+.l-main-section
+:marked
+ ### Angular NgModule Names
+ #### Style 02-12
+
+.s-rule.do
+ :marked
+ **Do** append the symbol name with the suffix `Module`.
+
+.s-rule.do
+ :marked
+ **Do** give the file name the `.module.ts` extension.
+
+.s-rule.do
+ :marked
+ **Do** name the module after the feature and folder it resides in.
+
+.s-why
+ :marked
+ **Why?** Provides a consistent way to quickly identify and reference modules.
+
+.s-why
+ :marked
+ **Why?** Upper camel case is conventional for identifying objects that can be instantiated using a constructor.
+
+.s-why.s-why-last
+ :marked
+ **Why?** Easily identifies the module as the root of the same named feature.
+
+.s-rule.do
+ :marked
+ **Do** suffix a _RoutingModule_ class name with `RoutingModule`.
+
+.s-rule.do
+ :marked
+ **Do** end the filename of a _RoutingModule_ with `-routing.module.ts`.
+
+.s-why.s-why-last
+ :marked
+ **Why?** A `RoutingModule` is a module dedicated exclusively to configuring the Angular router.
+ A consistent class and file name convention make these modules easy to spot and verify.
+- var top="vertical-align:top"
+table(width="100%")
+ col(width="50%")
+ col(width="50%")
+ tr
+ th Symbol Name
+ th File Name
+ tr(style=top)
+ td
+ code-example.
+ @NgModule({ ... })
+ export class AppModule { }
+ td
+ :marked
+ app.module.ts
+ tr(style=top)
+ td
+ code-example.
+ @NgModule({ ... })
+ export class HeroesModule { }
+ td
+ :marked
+ heroes.module.ts
+ tr(style=top)
+ td
+ code-example.
+ @NgModule({ ... })
+ export class VillainsModule { }
+ td
+ :marked
+ villains.module.ts
+ tr(style=top)
+ td
+ code-example.
+ @NgModule({ ... })
+ export class AppRoutingModule { }
+ td
+ :marked
+ app-routing.module.ts
+ tr(style=top)
+ td
+ code-example.
+ @NgModule({ ... })
+ export class HeroesRoutingModule { }
+ td
+ :marked
+ heroes-routing.module.ts
+:marked
+
+a(href="#toc") Back to top
+
.l-main-section
:marked
## Coding Conventions
@@ -640,12 +770,13 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** Classes can be instantiated and construct an instance. We often use upper camel case to indicate a constructable asset.
+ **Why?** Classes can be instantiated and construct an instance.
+ By convention, upper camel case indicates a constructable asset.
-+makeExample('style-guide/ts/03-01/app/shared/exception.service.avoid.ts', 'example', 'app/shared/exception.service.ts')(avoid=1)
++makeExample('style-guide/ts/03-01/app/core/exception.service.avoid.ts', 'example', 'app/shared/exception.service.ts')(avoid=1)
:marked
-+makeExample('style-guide/ts/03-01/app/shared/exception.service.ts', 'example', 'app/shared/exception.service.ts')
++makeExample('style-guide/ts/03-01/app/core/exception.service.ts', 'example', 'app/shared/exception.service.ts')
:marked
a(href="#toc") Back to top
@@ -665,7 +796,7 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- TypeScript helps enforce that intent by requiring immediate initialization and by
+ **Why?** TypeScript helps enforce that intent by requiring immediate initialization and by
preventing subsequent re-assignment.
.s-rule.consider
@@ -689,11 +820,11 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** Although we recommend creating _new_ constants in lower camel case,
- the tradition of UPPER_SNAKE_CASE remains popular and pervasive,
+ **Why?** The tradition of UPPER_SNAKE_CASE remains popular and pervasive,
especially in third party modules.
+ It is rarely worth the effort to change them or the risk of breaking existing code and documentation.
-+makeExample('style-guide/ts/03-02/app/shared/data.service.ts', '', 'app/shared/data.service.ts')
++makeExample('style-guide/ts/03-02/app/core/data.service.ts', '', 'app/shared/data.service.ts')
:marked
a(href="#toc") Back to top
@@ -711,14 +842,31 @@ a(href="#toc") Back to top
:marked
**Consider** naming an interface without an `I` prefix.
+.s-rule.consider
+ :marked
+ **Consider** using a class instead of an interface.
+
+.s-why
+ :marked
+ **Why?** TypeScript guidelines
+ discourage the "I" prefix.
+
+.s-why
+ :marked
+ **Why?** A class alone is less code than a _class-plus-interface_.
+
+.s-why
+ :marked
+ **Why?** A class can act as an interface (use `implements` instead of `extends`).
+
.s-why.s-why-last
:marked
- **Why?** When we use types, we can often simply use the class as the type.
+ **Why?** An interface-class can be a provider lookup token in Angular dependency injection.
-+makeExample('style-guide/ts/03-03/app/shared/hero-collector.service.avoid.ts', 'example', 'app/shared/hero-collector.service.ts')(avoid=1)
++makeExample('style-guide/ts/03-03/app/core/hero-collector.service.avoid.ts', 'example', 'app/shared/hero-collector.service.ts')(avoid=1)
:marked
-+makeExample('style-guide/ts/03-03/app/shared/hero-collector.service.ts', 'example', 'app/shared/hero-collector.service.ts')
++makeExample('style-guide/ts/03-03/app/core/hero-collector.service.ts', 'example', 'app/shared/hero-collector.service.ts')
:marked
a(href="#toc") Back to top
@@ -748,10 +896,10 @@ a(href="#toc") Back to top
:marked
**Why?** TypeScript tooling makes it easy to identify private vs public properties and methods.
-+makeExample('style-guide/ts/03-04/app/shared/toast.service.avoid.ts', 'example', 'app/shared/toast.service.ts')(avoid=1)
++makeExample('style-guide/ts/03-04/app/core/toast.service.avoid.ts', 'example', 'app/shared/toast.service.ts')(avoid=1)
:marked
-+makeExample('style-guide/ts/03-04/app/shared/toast.service.ts', 'example', 'app/shared/toast.service.ts')
++makeExample('style-guide/ts/03-04/app/core/toast.service.ts', 'example', 'app/shared/toast.service.ts')
:marked
a(href="#toc") Back to top
@@ -761,17 +909,17 @@ a(href="#toc") Back to top
### Import Line Spacing
#### Style 03-06
-.s-rule.do
+.s-rule.consider
:marked
- **Do** leave one empty line between third party imports and imports of code we created.
+ **Consider** leaving one empty line between third party imports and application imports.
-.s-rule.do
+.s-rule.consider
:marked
- **Do** list import lines alphabetized by the module.
+ **Consider** listing import lines alphabetized by the module.
-.s-rule.do
+.s-rule.consider
:marked
- **Do** list destructured imported assets alphabetically.
+ **Consider** listing destructured imported assets alphabetically.
.s-why
:marked
@@ -791,11 +939,17 @@ a(href="#toc") Back to top
.l-main-section
:marked
- ## Application Structure
+ ## App Structure and Angular Modules
- 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.
+ 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 `app`. All content is 1 feature per file. Each component, service, and pipe is in its own file. All 3rd party vendor scripts are stored in another folder and not in the `app` folder. We didn't write them and we don't want them cluttering our app. Use the naming conventions for files in this guide.
+ All of the app's code goes in a folder named `app`.
+ All feature areas are in their own folder, with their own Angular module.
+
+ All content is 1 asset per file. Each component, service, and pipe is in its own file.
+ All 3rd party vendor scripts are stored in another folder and not in the `app` folder.
+ You didn't write them and you don't want them cluttering app.
+ Use the naming conventions for files in this guide.
a(href="#toc") Back to top
@@ -806,7 +960,10 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** structure the app such that we can `L`ocate our code quickly, `I`dentify the code at a glance, keep the `F`lattest structure we can, and `T`ry to be DRY.
+ **Do** structure the app such that you can `L`ocate code quickly,
+ `I`dentify the code at a glance,
+ keep the `F`lattest structure you can, and
+ `T`ry to be DRY.
.s-rule.do
:marked
@@ -814,7 +971,9 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** LIFT Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly. Another way to check our app structure is to ask ourselves: How quickly can we open and work in all of the related files for a feature?
+ **Why?** LIFT Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly.
+ To confirm your intuition about a particular structure, ask:
+ _can I quickly open and start work in all of the related files for this feature_?
a(href="#toc") Back to top
@@ -825,11 +984,15 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** make locating our code intuitive, simple and fast.
+ **Do** make locating code intuitive, simple and fast.
.s-why.s-why-last
:marked
- **Why?** We find this to be super important for a project. If we cannot find the files we need to work on quickly, we will not be able to work as efficiently as possible, and the structure will need to change. We may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time. A descriptive folder structure can help with this.
+ **Why?**
+ To work efficiently you must be able to find files quickly,
+ especially when you do not know (or do not remember) the file _names_.
+ Keeping related files near each other in an intuitive location saves time.
+ A descriptive folder structure makes a world of difference to you and the people who come after you.
a(href="#toc") Back to top
@@ -840,7 +1003,7 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** name the file such that we instantly know what it contains and represents.
+ **Do** name the file such that you instantly know what it contains and represents.
.s-rule.do
:marked
@@ -852,11 +1015,14 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** We spend less time hunting and pecking for code, and become more efficient. If this means we want longer file names, then so be it.
+ **Why?** Spend less time hunting and pecking for code, and become more efficient.
+ Longer file names are far better than _short-but-obscure_ abbreviated names.
.l-sub-section
:marked
- There are deviations of the 1 per file rule when we have a set of very small features that are all related to each other, as they are still easily identifiable.
+ It may be advantageous to deviate from the _one-thing-per-file_ rule when
+ you have a set of small, closely-related features that are better discovered and understood
+ in a single file than as multiple files. Be wary of this loophole.
a(href="#toc") Back to top
@@ -871,11 +1037,24 @@ a(href="#toc") Back to top
.s-rule.consider
:marked
- **Consider** creating folders when we get to seven or more files.
+ **Consider** creating sub-folders when a folder reaches seven or more files.
-.s-why.s-why-last
+.s-rule.consider
:marked
- **Why?** Nobody wants to search seven levels of folders to find a file. In a folder structure there is no hard and fast number rule, but when a folder has seven to ten files, that may be time to create subfolders. We base it on our comfort level. Use a flatter structure until there is an obvious value (to help the rest of LIFT) in creating a new folder.
+ **Consider** configuring the IDE to hide distracting, irrelevant files such as generated `.js` and `.js.map` files.
+
+s-why.s-why-last
+ :marked
+ **Why?** No one wants to search for a file through seven levels of folders.
+ A flat structure is easy to scan.
+
+ On the other hand,
+ psychologists believe
+ that humans start to struggle when the number of adjacent interesting things exceeds nine.
+ So when a folder has ten or more files, it may be time to create subfolders.
+
+ Base your decision on your comfort level.
+ Use a flatter structure until there is an obvious value to creating a new folder.
a(href="#toc") Back to top
@@ -890,11 +1069,14 @@ a(href="#toc") Back to top
.s-rule.avoid
:marked
- **Avoid** being so DRY that we sacrifice readability.
+ **Avoid** being so DRY that you sacrifice readability.
.s-why.s-why-last
:marked
- **Why?** Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why we call it T-DRY. We don’t want to type `hero-view.component.html` for a view because, well, it’s obviously a view. If it is not obvious or by convention, then we name it.
+ **Why?** Being DRY is important, but not crucial if it sacrifices the other elements of LIFT.
+ That's why its calle _T-DRY_.
+ For example, it's redundant to name a component, `hero-view.component.html` because a component is obviously a view.
+ But if something is not obvious or departs from a convention, then spell it out.
a(href="#toc") Back to top
@@ -917,43 +1099,69 @@ a(href="#toc") Back to top
.s-rule.consider
:marked
- **Consider** creating a folder for each component including its `.ts`, `.html`, `.css` and `.spec` file.
+ **Consider** creating a folder for a component when is has multiple accompanying files (`.ts`, `.html`, `.css` and `.spec`).
.s-why
:marked
- **Why?** Helps us keep the app structure small and easy to maintain in the early stages, while being easy to evolve as the app grows.
+ **Why?** Helps keep the app structure small and easy to maintain in the early stages, while being easy to evolve as the app grows.
.s-why.s-why-last
:marked
**Why?** Components often have four files (e.g. `*.html`, `*.css`, `*.ts`, and `*.spec.ts`) and can clutter a folder quickly.
-.example-title Overall Folder and File Structure
+a(id='file-tree')
+:marked
+ Folder and File Structure
+
.filetree
.file src
.children
.file app
.children
+ .file core
+ .children
+ .file core.module.ts
+ .file exception.service.ts|spec.ts
+ .file user-profile.service.ts|spec.ts
.file heroes
.children
.file hero
.children
.file hero.component.ts|html|css|spec.ts
- .file index.ts
.file hero-list
.children
.file hero-list.component.ts|html|css|spec.ts
- .file index.ts
.file shared
.children
+ .file hero-button.component.ts|html|css|spec.ts
.file hero.model.ts
.file hero.service.ts|spec.ts
- .file index.ts
.file heroes.component.ts|html|css|spec.ts
- .file index.ts
+ .file heroes.module.ts
+ .file heroes-routing.module.ts
.file shared
.children
- .file ...
+ .file shared.module.ts
+ .file init-caps.pipe.ts|spec.ts
+ .file text-filter.component.ts|spec.ts
+ .file text-filter.service.ts|spec.ts
+ .file villains
+ .children
+ .file villain
+ .children
+ .file ...
+ .file villain-list
+ .children
+ .file ...
+ .file shared
+ .children
+ .file ...
+ .file villains.component.ts|html|css|spec.ts
+ .file villains.module.ts
+ .file villains-routing.module.ts
.file app.component.ts|html|css|spec.ts
+ .file app.module.ts
+ .file app-routing.module.ts
.file main.ts
.file index.html
.file ...
@@ -961,78 +1169,21 @@ a(href="#toc") Back to top
.l-sub-section
:marked
- While we prefer our Components to be in their own dedicated folder, another option for small apps is to keep Components flat (not in a dedicated folder). This adds up to four files to the existing folder, but also reduces the folder nesting. Be consistent.
+ While components in dedicated folder are widely preferred,
+ another option for small apps is to keep components flat (not in a dedicated folder).
+ This adds up to four files to the existing folder, but also reduces the folder nesting.
+ Whatever you choose, be consistent.
a(href="#toc") Back to top
.l-main-section
:marked
- ### Shared Folder
+ ### Folders-by-Feature Structure
#### Style 04-07
.s-rule.do
:marked
- **Do** put all shared files within a component feature in a `shared` folder.
-
-.s-rule.consider
- :marked
- **Consider** creating a folder for each component including its `.ts`, `.html`, `.css` and `.spec` file.
-
-.s-why
- :marked
- **Why?** Separates shared files from the components within a feature.
-
-.s-why.s-why-last
- :marked
- **Why?** Makes it easier to locate shared files within a component feature.
-
-.example-title Shared Folder
-.filetree
- .file src
- .children
- .file app
- .children
- .file heroes
- .children
- .file hero
- .children
- .file ...
- .file hero-list
- .children
- .file ...
- .file shared
- .children
- .file hero-button
- .children
- .file ...
- .file hero.model.ts
- .file hero.service.ts|spec.ts
- .file index.ts
- .file heroes.component.ts|html|css|spec.ts
- .file index.ts
- .file shared
- .children
- .file exception.service.ts|spec.ts
- .file index.ts
- .file nav
- .children
- .file ...
- .file app.component.ts|html|css|spec.ts
- .file main.ts
- .file index.html
- .file ...
-:marked
-
-a(href="#toc") Back to top
-
-.l-main-section
-:marked
- ### Folders-by-Feature Structure
- #### Style 04-08
-
-.s-rule.do
- :marked
- **Do** create folders named for the feature they represent.
+ **Do** create folders named for the feature area they represent.
.s-why
:marked
@@ -1046,202 +1197,346 @@ a(href="#toc") Back to top
:marked
**Why?** Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines.
-.s-why.s-why-last
+.s-why
:marked
- **Why?** When there are a lot of files (e.g. 10+) locating them is easier with a consistent folder structures and more difficult in flat structures.
-
-:marked
- Below is an example of a small app with folders per component.
-
-.example-title Folders per Component
-.filetree
- .file src
- .children
- .file app
- .children
- .file heroes
- .children
- .file hero
- .children
- .file ...
- .file hero-list
- .children
- .file ...
- .file shared
- .children
- .file ...
- .file heroes.component.ts|html|css|spec.ts
- .file index.ts
- .file villains
- .children
- .file villain
- .children
- .file ...
- .file villain-list
- .children
- .file ...
- .file shared
- .children
- .file ...
- .file villains.component.ts|html|css|spec.ts
- .file index.ts
- .file shared
- .children
- .file nav
- .children
- .file ...
- .file ...
- .file app.component.ts|html|css|spec.ts
- .file main.ts
- .file index.html
- .file ...
-:marked
-
-a(href="#toc") Back to top
-
-.l-main-section
-:marked
- ### Layout Components
- #### Style 04-09
+ **Why?** When there are a lot of files (e.g. 10+), locating them is easier with a consistent folder structure and more difficult in a flat structure.
.s-rule.do
:marked
- **Do** put components that define the overall layout in a `shared` folder.
-
-.s-rule.do
- :marked
- **Do** put shared layout components in their own folder, under the `shared` folder.
+ **Do** create an Angular module for each feature area.
.s-why
:marked
- **Why?** We need a place to host our layout for our app. Our navigation bar, footer, and other aspects of the app that are needed for the entire app.
+ **Why?** Angular modules make it easy to lazy load routable features.
.s-why.s-why-last
:marked
- **Why?** Organizes all layout in a consistent place re-used throughout the application.
+ **Why?** Angular modules make it easier to isolate, test, and re-use features.
-.example-title Folder for Layout Components
-.filetree
- .file src
- .children
- .file app
- .children
- .file heroes
- .children
- .file ...
- .file shared
- .children
- .file nav
- .children
- .file index.ts
- .file nav.component.ts|html|css|spec.ts
- .file footer
- .children
- .file index.ts
- .file footer.component.ts|html|css|spec.ts
- .file index.ts
- .file ...
- .file app.component.ts|html|css|spec.ts
- .file main.ts
- .file index.html
- .file ...
+.file-tree-reference
+ a(href="#file-tree") Refer here to this Folder and File Structure example
+
+a(href="#toc") Back to top
+ :marked
+
+.l-main-section
+:marked
+ ### App Root Module
+ #### Style 04-08
+
+.s-rule.do
+ :marked
+ **Do** create an Angular module at the root of the application.
+
+.s-why
+ :marked
+ **Why?** Every app requires at least one Angular module.
+
+.s-rule.consider
+ :marked
+ **Consider** naming the root module `app.module.ts`.
+
+.s-why.s-why-last
+ :marked
+ **Why?** Makes it easier to locate and identify the root module.
+
++makeExample('style-guide/ts/04-08/app/app.module.ts', 'example', 'app/app.module.ts')
:marked
a(href="#toc") Back to top
.l-main-section
:marked
- ### Create and Import Barrels
+ ### Feature Modules
+ #### Style 04-09
+.s-rule.do
+
+ :marked
+ **Do** create an Angular module for all distinct features in an application (e.g. `Heroes` feature).
+
+.s-rule.do
+ :marked
+ **Do** place the feature module in the same named folder as the feature area (.e.g `app/heroes`).
+
+.s-rule.do
+ :marked
+ **Do** name the feature module file reflecting the name of the feature area and folder (e.g. `app/heroes/heroes.module.ts`)
+
+.s-rule.do
+ :marked
+ **Do** name the feature module symbol reflecting the name of the feature area, folder, and file (e.g. `app/heroes/heroes.module.ts` defines `HeroesModule`)
+
+.s-why
+ :marked
+ **Why?** A feature module can expose or hide its implementation from other modules.
+
+.s-why
+ :marked
+ **Why?** A feature module identifies distinct sets of related components that comprise the feature area.
+
+.s-why
+ :marked
+ **Why?** A feature module can easily be routed to both eagerly and lazily.
+
+.s-why
+ :marked
+ **Why?** A feature module defines clear boundaries between specific functionality and other application features.
+
+.s-why
+ :marked
+ **Why?** A feature module helps clarify and make it easier to assign development responsibilities to different teams.
+
+.s-why.s-why-last
+ :marked
+ **Why?** A feature module can easily be isolated for testing.
+
+a(href="#toc") Back to top
+
+.l-main-section
+:marked
+ ### Shared Feature Module
#### Style 04-10
-.s-rule.consider
+.s-rule.do
:marked
- **Consider** creating a file that imports, aggregates, and re-exports items. We call this technique a **barrel**.
+ **Do** create a feature module named `SharedModule` in a `shared` folder (e.g. `app/shared/shared.module.ts` defines `SharedModule`).
-.s-rule.consider
+.s-rule.do
:marked
- **Consider** naming this barrel file `index.ts`.
+ **Do** put common components, directives and pipes that will be used throughout the application by other feature modules in the `SharedModule`, where those assets are expected to share a new instance of themselves (not singletons).
+
+.s-rule.do
+ :marked
+ **Do** import all modules required by the assets in the `SharedModule` (e.g. `CommonModule` and `FormsModule`).
.s-why
:marked
- **Why?** A barrel aggregates many imports into a single import.
+ **Why?** `SharedModule` will contain components, directives and pipes that may need features from another common module (e.g. `ngFor` in `CommonModule`).
+
+.s-rule.do
+ :marked
+ **Do** declare all components, directives, and pipes in the `SharedModule`.
+
+.s-rule.do
+ :marked
+ **Do** export all symbols that from the `SharedModule` that other feature modules need to use.
.s-why
:marked
- **Why?** A barrel reduces the number of imports a file may need.
-
+ **Why?** `SharedModule` exists to make commonly used components, directives and pipes available for use in the templates of components in many other modules.
+
+.s-rule.avoid
+ :marked
+ **Avoid** specifying app-wide singleton providers in a `SharedModule`. Intentional singletons are OK. Take care.
+
.s-why
:marked
- **Why?** A barrel provides a consistent pattern to import everything exported in the barrel from a folder.
-
-.s-why
- :marked
- **Why?** This is consistent with a pattern from Node, which imports the index.js|ts file from a folder.
+ **Why?** A lazy loaded feature module that imports that shared module will make its own copy of the service and likely have undesireable results.
.s-why.s-why-last
:marked
- **Why?** A barrel shortens import statements.
+ **Why?** You don't want each module to have its own separate instance of singleton services.
+ Yet there is a real danger of that happening if the `SharedModule` provides a service.
-+makeTabs(
- `style-guide/ts/04-10/app/shared/index.ts,
- style-guide/ts/04-10/app/shared/filter-text/index.ts,
- style-guide/ts/04-10/app/shared/modal/index.ts,
- style-guide/ts/04-10/app/shared/nav/index.ts,
- style-guide/ts/04-10/app/shared/spinner/index.ts,
- style-guide/ts/04-10/app/shared/toast/index.ts`,
- `example,,,,,`,
- `app/shared/index.ts,
- app/shared/filter-text/index.ts,
- app/shared/modal/index.ts,
- app/shared/nav/index.ts,
- app/shared/spinner/index.ts,
- app/shared/toast/index.ts`)
-:marked
-
-.example-title Folder Barrels
.filetree
.file src
.children
.file app
.children
- .file dashboard
- .children
- .file ...
- .file index.ts
- .file heroes
- .children
- .file ...
- .file index.ts
.file shared
.children
- .file nav
- .children
- .file ...
- .file index.ts
- .file search
- .children
- .file ...
- .file index.ts
- .file ...
- .file index.ts
+ .file shared.module.ts
+ .file init-caps.pipe.ts|spec.ts
+ .file text-filter.component.ts|spec.ts
+ .file text-filter.service.ts|spec.ts
.file app.component.ts|html|css|spec.ts
+ .file app.module.ts
+ .file app-routing.module.ts
.file main.ts
.file index.html
.file ...
:marked
-+makeExample('style-guide/ts/04-10/app/heroes/heroes.component.avoid.ts', 'example', 'app/heroes/heroes.component.ts')(avoid=1)
-:marked
-
-+makeExample('style-guide/ts/04-10/app/heroes/heroes.component.ts', 'example', 'app/heroes/heroes.component.ts')
++makeTabs(
+ `style-guide/ts/04-10/app/shared/shared.module.ts,
+ style-guide/ts/04-10/app/shared/init-caps.pipe.ts,
+ style-guide/ts/04-10/app/shared/filter-text/filter-text.component.ts,
+ style-guide/ts/04-10/app/shared/filter-text/filter-text.service.ts,
+ style-guide/ts/04-10/app/heroes/heroes.component.ts,
+ style-guide/ts/04-10/app/heroes/heroes.component.html,
+ `,
+ `,,,`,
+ `app/shared/shared.module.ts,
+ app/shared/init-caps.pipe.ts,
+ app/shared/filter-text/filter-text.component.ts,
+ app/shared/filter-text/filter-text.service.ts,
+ app/heroes/heroes.component.ts,
+ app/heroes/heroes.component.html,
+ `)
:marked
a(href="#toc") Back to top
.l-main-section
:marked
- ### Lazy Loaded Folders
+ ### Core Feature Module
#### Style 04-11
+
+.s-rule.do
+ :marked
+ **Do** collect single-use classes and hiding their gory details inside `CoreModule`. A simplified root `AppModule` imports `CoreModule` in its capacity as orchestrator of the application as a whole.
+
+.s-rule.do
+ :marked
+ **Do** create a feature module named `CoreModule` in a `core` folder (e.g. `app/core/core.module.ts` defines `CoreModule`).
+
+.s-rule.do
+ :marked
+ **Do** put a singleton service whose instance wil be shared throughout the application in the `CoreModule` (e.g. `ExceptionService` and `LoggerService`).
+
+.s-rule.do
+ :marked
+ **Do** import all modules required by the assets in the `CoreModule` (e.g. `CommonModule` and `FormsModule`).
+
+.s-why
+ :marked
+ **Why?** `CoreModule` provides one or more singleton services. Angular registers the providers with the app root injector, making a singleton instance of each service available to any component that needs them, whether that component is eagerly or lazily loaded.
+
+.s-why
+ :marked
+ **Why?** `CoreModule` will contain singleton services. When a lazy loaded module imports these, it will get a new instance and not the intended app-wide singleton.
+
+.s-rule.do
+ :marked
+ **Do** gather application-wide, single use components in the `CoreModule`.
+ Import it once (in the `AppModule`) when the app starts and never import it anywhere else. (e.g. `NavComponent` and `SpinnerComponent`).
+
+.s-why
+ :marked
+ **Why?** Real world apps can have several single-use components (e.g., spinners, message toasts, and modal dialogs) that appear only in the `AppComponent` template.
+ They are not imported elsewhere so they're not shared in that sense.
+ Yet they're too big and messy to leave loose in the root folder.
+
+.s-rule.avoid
+ :marked
+ **Avoid** importing the `CoreModule` anywhere except in the `AppModule`.
+
+.s-why
+ :marked
+ **Why?** A lazily loaded feature module that directly imports the `CoreModule` will make its own copy of services and likely have undesireable results.
+
+.s-why
+ :marked
+ **Why?** An eagerly loaded feature module already has access to the `AppModule`'s injector, and thus the `CoreModule`'s services.
+
+.s-rule.do
+ :marked
+ **Do** export all symbols that from the `CoreModule` that the `AppModule` will import and make available for other feature modules to use.
+
+.s-why
+ :marked
+ **Why?** `CoreModule` exists to make commonly used singleton services available for use in the many other modules.
+
+.s-why.s-why-last
+ :marked
+ **Why?** You wnat the entire app to use the one, singleton instance.
+ You don't want each module to have its own separate instance of singleton services.
+ Yet there is a real danger of that happening accidentally if the `CoreModule` provides a service.
+
+
+.filetree
+ .file src
+ .children
+ .file app
+ .children
+ .file core
+ .children
+ .file core.module.ts
+ .file logger.service.ts|spec.ts
+ .file nav
+ .children
+ .file nav.component.ts|html|css|spec.ts
+ .file spinner
+ .children
+ .file spinner.component.ts|html|css|spec.ts
+ .file spinner.service.ts|spec.ts
+ .file app.component.ts|html|css|spec.ts
+ .file app.module.ts
+ .file app-routing.module.ts
+ .file main.ts
+ .file index.html
+ .file ...
+:marked
+
++makeTabs(
+ `
+ style-guide/ts/04-11/app/app.module.ts,
+ style-guide/ts/04-11/app/core/core.module.ts,
+ style-guide/ts/04-11/app/core/logger.service.ts,
+ style-guide/ts/04-11/app/core/nav/nav.component.ts,
+ style-guide/ts/04-11/app/core/nav/nav.component.html,
+ style-guide/ts/04-11/app/core/spinner/spinner.component.ts,
+ style-guide/ts/04-11/app/core/spinner/spinner.component.html,
+ style-guide/ts/04-11/app/core/spinner/spinner.service.ts
+ `,
+ `example,,,,,,,`,
+ `
+ app/app.module.ts,
+ app/core/core.module.ts,
+ app/core/logger.service.ts,
+ app/core/nav/nav.component.ts,
+ app/core/nav/nav.component.html,
+ app/core/spinner/spinner.component.ts,
+ app/core/spinner/spinner.component.html,
+ app/core/spinner/spinner.service.ts
+ `)
+:marked
+
+.l-sub-section
+ :marked
+ `AppModule` is a little smaller because many app/root classes have moved to other modules.
+ `AppModule` is stable because you will add future components and providers to other modules, not this one.
+ `AppModule` delegates to imported modules rather than doing work.
+ `AppModule` is focused on its main task, orchestrating the app as a whole.
+
+a(href="#toc") Back to top
+
+.l-main-section
+:marked
+ ### Prevent Reimport of Core Module
+ #### Style 04-12
+ Only the root `AppModule` should import the `CoreModule`.
+
+.s-rule.do
+ :marked
+ **Do** guard against reimporting of `CoreModule` and fail fast by adding guard logic.
+
+.s-why.s-why
+ :marked
+ **Why?** Guards against reimporting of the `CoreModule`.
+
+.s-why.s-why-last
+ :marked
+ **Why?** Guards against creating multiple instances of assets intended to be singletons.
+
++makeTabs(
+ `
+ style-guide/ts/04-12/app/core/module-import-guard.ts,
+ style-guide/ts/04-12/app/core/core.module.ts
+ `,
+ `,`,
+ `
+ app/core/module-import-guard,
+ app/core/core.module.ts
+ `)
+:marked
+
+a(href="#toc") Back to top
+
+.l-main-section
+:marked
+ ### Lazy Loaded Folders
+ #### Style 04-13
A distinct application feature or workflow may be *lazy loaded* or *loaded on demand* rather than when the application starts.
.s-rule.do
@@ -1257,8 +1552,8 @@ a(href="#toc") Back to top
.l-main-section
:marked
- ### Never Directly Import Lazy Loaded Folders
- #### Style 04-13
+ ### Never Directly Import Lazy Loaded Folders
+ #### Style 04-14
.s-rule.avoid
:marked
@@ -1266,28 +1561,7 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** Directly importing and using a module loads it immediately when our intention is to load it on demand.
-
-+makeExample('style-guide/ts/04-13/app/app.component.avoid.ts', 'example', 'app/app.component.ts')(avoid=1)
-:marked
-
-a(href="#toc") Back to top
-
-.l-main-section
-:marked
- ### Lazy Loaded Folders May Import From a Parent
- #### Style 04-14
-
-.s-rule.do
- :marked
- **Do** allow lazy loaded modules to import a module from a parent folder.
-
-.s-why.s-why-last
- :marked
- **Why?** A parent module has already been loaded by the time the lazy loaded module imports it.
-
-+makeExample('style-guide/ts/04-14/app/heroes/heroes.component.ts', 'example', 'app/heroes/heroes.component.ts')
-:marked
+ **Why?** Directly importing and using a module will load it immediately when the intention is to load it on demand.
a(href="#toc") Back to top
@@ -1295,12 +1569,12 @@ a(href="#toc") Back to top
:marked
## Components
- ### Components Selector Naming
+ ### Component Selector Naming
#### Style 05-02
.s-rule.do
:marked
- **Do** use `kebab-case` for naming the element selectors of our components.
+ **Do** use _dashed-case_ or _kebab-case_ for naming the element selectors of components.
.s-why.s-why-last
:marked
@@ -1326,15 +1600,16 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** define Components as elements via the selector.
+ **Do** define components as elements via the selector.
.s-why
:marked
- **Why?** Components have templates containing HTML and optional Angular template syntax. They are most associated with putting content on a page, and thus are more closely aligned with elements.
+ **Why?** components have templates containing HTML and optional Angular template syntax. They are most associated with putting content on a page, and thus are more closely aligned with elements.
.s-why
:marked
- **Why?** Components are derived from Directives, and thus their selectors can be elements, attributes, or other selectors. Defining the selector as an element provides consistency for components that represent content with a template.
+ **Why?** A component represents a visual element on the page.
+ Defining the selector as an HTML element tag is consistent with native HTML elements and WebComponents.
.s-why.s-why-last
:marked
@@ -1367,11 +1642,11 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** name the template file `[component-name].component.html`, where [component-name] is our component name.
+ **Do** name the template file `[component-name].component.html`, where [component-name] is the component name.
.s-rule.do
:marked
- **Do** name the style file `[component-name].component.css`, where [component-name] is our component name.
+ **Do** name the style file `[component-name].component.css`, where [component-name] is the component name.
.s-why
:marked
@@ -1415,7 +1690,10 @@ a(href="#toc") Back to top
.s-why
:marked
- **Why?** If we ever need to rename the property or event name associated to [`@Input`](https://angular.io/docs/ts/latest/api/core/index/Input-var.html) or [`@Output`](https://angular.io/docs/ts/latest/api/core/index/Output-var.html) we can modify it on a single place.
+ **Why?** If you ever need to rename the property or event name associated to
+ [`@Input`](https://angular.io/docs/ts/latest/api/core/index/Input-var.html) or
+ [`@Output`](https://angular.io/docs/ts/latest/api/core/index/Output-var.html)
+ you can modify it on a single place.
.s-why
:marked
@@ -1477,7 +1755,8 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** Placing members in a consistent sequence makes it easy to read and helps we instantly identify which members of the component serve which purpose.
+ **Why?** Placing members in a consistent sequence makes it easy to read and
+ helps instantly identify which members of the component serve which purpose.
+makeExample('style-guide/ts/05-14/app/shared/toast/toast.component.avoid.ts', 'example', 'app/shared/toast/toast.component.ts')(avoid=1)
:marked
@@ -1535,7 +1814,7 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** name our event handler methods with the prefix `on` followed by the event name.
+ **Do** name event handler methods with the prefix `on` followed by the event name.
.s-why
:marked
@@ -1568,11 +1847,11 @@ a(href="#toc") Back to top
.s-rule.do
:marked
- **Do** put presentation logic in the Component class, and not in the template.
+ **Do** put presentation logic in the component class, and not in the template.
.s-why
:marked
- **Why?** Logic will be contained in one place (the Component class) instead of being spread in two places.
+ **Why?** Logic will be contained in one place (the component class) instead of being spread in two places.
.s-why.s-why-last
:marked
@@ -1622,22 +1901,31 @@ a(href="#toc") Back to top
### Use HostListener and HostBinding Class Decorators
#### Style 06-03
+.s-rule.consider
+ :marked
+ **Consider** preferring the `@HostListener` and `@HostBinding` to the
+ `host` property of the `@Directive` and `@Component` decorators.
+
.s-rule.do
:marked
- **Do** use @HostListener and @HostBinding instead of the host property of the @Directive and @Component decorators:
-
-.s-why
- :marked
- **Why?** The property or method name associated with @HostBinding or respectively @HostListener should be modified only in a single place - in the directive's class. In contrast if we use host we need to modify both the property declaration inside the controller, and the metadata associated to the directive.
+ **Do** be consistent in your choice.
.s-why.s-why-last
:marked
- **Why?** The metadata declaration attached to the directive is shorter and thus more readable.
+ **Why?** The property associated with `@HostBinding` or the method associated with `@HostListener`
+ can be modified only in a single place - in the directive's class.
+ If you use the `host` metadata property, you must modify both the property declaration inside the controller,
+ and the metadata associated with the directive.
-+makeExample('style-guide/ts/06-03/app/shared/validator.directive.avoid.ts', 'example', 'app/shared/validator.directive.ts')(avoid=1)
++makeExample('style-guide/ts/06-03/app/shared/validator.directive.ts', '', 'app/shared/validator.directive.ts')
:marked
+ Compare with the less preferred `host` metadata alternative.
-+makeExample('style-guide/ts/06-03/app/shared/validator.directive.ts', 'example', 'app/shared/validator.directive.ts')
+.s-why.s-why-last
+ :marked
+ **Why?** The `host` metadata is only one term to remember and doesn't require extra ES imports.
+
++makeExample('style-guide/ts/06-03/app/shared/validator2.directive.ts', '', 'app/shared/validator2.directive.ts')
:marked
a(href="#toc") Back to top
@@ -1646,7 +1934,7 @@ a(href="#toc") Back to top
:marked
## Services
- ### Services are Singletons in Same Injector
+ ### Services are Singletons within an Injector
#### Style 07-01
.s-rule.do
@@ -1685,7 +1973,7 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** When a service has multiple responsibilities, every Component or Service that injects it now carries the weight of them all.
+ **Why?** When a service has multiple responsibilities, every component or service that injects it now carries the weight of them all.
a(href="#toc") Back to top
@@ -1735,7 +2023,7 @@ a(href="#toc") Back to top
.s-why
:marked
- **Why?** The Angular DI mechanism resolves all the dependencies of our services based on their types declared with the services' constructors.
+ **Why?** The Angular DI mechanism resolves all dependencies of services based on their types declared with the services' constructors.
.s-why.s-why-last
:marked
@@ -1797,8 +2085,8 @@ a(href="#toc") Back to top
.s-why.s-why-last
:marked
- **Why?** We get strong typing for the method signatures.
- The compiler and editor can call our attention to misspellings.
+ **Why?** Strongly-typed method signatures.
+ The compiler and editor can call out misspellings.
+makeExample('style-guide/ts/09-01/app/heroes/shared/hero-button/hero-button.component.avoid.ts', 'example', 'app/heroes/shared/hero-button/hero-button.component.ts')(avoid=1)
:marked
diff --git a/public/resources/css/module/_style-guide.scss b/public/resources/css/module/_style-guide.scss
index 1ac9dd564b..4aa267f3a0 100644
--- a/public/resources/css/module/_style-guide.scss
+++ b/public/resources/css/module/_style-guide.scss
@@ -44,3 +44,8 @@
table tr code-example .prettyprint {
margin-bottom: 0;
}
+
+.file-tree-reference {
+ margin-bottom: 20px;
+ margin-top: 20px;
+}