From 9c2f0c51df9df0477ca7a2e09cb561b3178d5953 Mon Sep 17 00:00:00 2001 From: Jay McDoniel Date: Sat, 1 Oct 2022 16:11:22 -0700 Subject: [PATCH 1/4] feat: add a new page for advanced topics Currently only discusses how `@Injectable()` works. We can add more in as time goes. --- content/fundamentals/advanced.md | 93 +++++++++++++++++++ src/app/homepage/menu/menu.component.ts | 1 + .../advanced/advanced.component.ts | 9 ++ .../pages/fundamentals/fundamentals.module.ts | 7 ++ 4 files changed, 110 insertions(+) create mode 100644 content/fundamentals/advanced.md create mode 100644 src/app/homepage/pages/fundamentals/advanced/advanced.component.ts diff --git a/content/fundamentals/advanced.md b/content/fundamentals/advanced.md new file mode 100644 index 0000000000..585c6e5717 --- /dev/null +++ b/content/fundamentals/advanced.md @@ -0,0 +1,93 @@ +### Advanced Concepts + +So far we've breezed over a couple of advanced topics in Typescript like decorators and how the DI system is actually working under the hood. If all you want is a system that works, ou can probably move on to another section, but if you want to get a deeper understanding of how Nest works, this is the section for you. + +#### The @Injectable() Decorator + +Earlier in the docs we mentioned that "The `@Injectable()` decorator attaches metadata, which declares that `CatsService` is a class that can be managed by the Nest IoC container.". This is only a half truth, in reality, adding the provider to a module's `providers` array is what makes the provider able to be injected via the IoC container. What `@njectable()` is doing is forcing Typescript to emit metadata about the class's `constructor`, specifically the `design:paramtypes` metadata that will be read at start up. If there is an `@Inject()` decorator in the constructor, by technicality this does enough to make Typescript emit all the same metadata. Take a look at the compiled JavaScript from the following Typescript class: + +```typescript +export class Foo { + constructor(private readonly foo: string) {} +} + +export class Bar { + constructor(private readonly bar: string) {} +} + +export class FooBar { + constructor(private readonly foo: Foo, private readonly bar: Bar) {} +} +``` + +The transpiled code would look like this + +```javascript +export class Foo { + constructor(foo) { + this.foo = foo; + } +} +export class Bar { + constructor(bar) { + this.bar = bar; + } +} +export class FooBar { + constructor(foo, bar) { + this.foo = foo; + this.bar = bar; + } +} +``` + +Now, let's add the `@Injectable()` decorator to the classes. We get the following output in our `dist` + +```javascript +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +let Foo = class Foo { + constructor(foo) { + this.foo = foo; + } +}; +Foo = __decorate([ + Injectable(), + __metadata("design:paramtypes", [String]) +], Foo); +export { Foo }; +let Bar = class Bar { + constructor(bar) { + this.bar = bar; + } +}; +Bar = __decorate([ + Injectable(), + __metadata("design:paramtypes", [String]) +], Bar); +export { Bar }; +let FooBar = class FooBar { + constructor(foo, bar) { + this.foo = foo; + this.bar = bar; + } +}; +FooBar = __decorate([ + Injectable(), + __metadata("design:paramtypes", [Foo, Bar]) +], FooBar); +export { FooBar }; +``` + +There's a lot going on in the above snippet, so let's ignore the `__decorate` and `__metadata` method definitions and look just at the class portions. Notice that now there are metadata definitions for the `design:paramtypes` metadata key of `[String]` for `Foo` and `Bar` and `[Foo, Bar]` for `FooBar`. This tells Nest what parameters are expected in each position of the constructor for each class. + +> info **Hint** When you use `@Inject('SomeString')` Nest will set `design:paramtypes` to `SomeString` for the position that you are decorating. + +What Nest will do when reading this metadata is match it up with the class or injection token that exists in the current module's provider list and use that provider in the proper location for the constructor. This is why it is very important to either use a class, or `@Inject()` on the right parameter, and why all decorators cannot be used wherever you want. diff --git a/src/app/homepage/menu/menu.component.ts b/src/app/homepage/menu/menu.component.ts index b37917abed..5481b1e224 100644 --- a/src/app/homepage/menu/menu.component.ts +++ b/src/app/homepage/menu/menu.component.ts @@ -80,6 +80,7 @@ export class MenuComponent implements OnInit { path: '/fundamentals/platform-agnosticism', }, { title: 'Testing', path: '/fundamentals/testing' }, + { title: 'Advanced', path: '/fundamentals/advanced' }, ], }, { diff --git a/src/app/homepage/pages/fundamentals/advanced/advanced.component.ts b/src/app/homepage/pages/fundamentals/advanced/advanced.component.ts new file mode 100644 index 0000000000..ca080ff4e0 --- /dev/null +++ b/src/app/homepage/pages/fundamentals/advanced/advanced.component.ts @@ -0,0 +1,9 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { BasePageComponent } from '../../page/page.component'; + +@Component({ + selector: 'app-advanced', + templateUrl: './advanced.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AdvancedComponent extends BasePageComponent {} diff --git a/src/app/homepage/pages/fundamentals/fundamentals.module.ts b/src/app/homepage/pages/fundamentals/fundamentals.module.ts index 0567d6300c..fb6e41df75 100644 --- a/src/app/homepage/pages/fundamentals/fundamentals.module.ts +++ b/src/app/homepage/pages/fundamentals/fundamentals.module.ts @@ -13,6 +13,7 @@ import { PlatformAgnosticismComponent } from './platform-agnosticism/platform-ag import { ProviderScopesComponent } from './provider-scopes/provider-scopes.component'; import { UnitTestingComponent } from './unit-testing/unit-testing.component'; import { LazyLoadingModulesComponent } from './lazy-loading-modules/lazy-loading-modules.component'; +import { AdvancedComponent } from './advanced/advanced.component'; const routes: Routes = [ { @@ -86,6 +87,11 @@ const routes: Routes = [ component: CircularDependencyComponent, data: { title: 'Circular Dependency' }, }, + { + path: 'advanced', + component: AdvancedComponent, + data: { title: 'Advanced Concepts' }, + }, ]; @NgModule({ @@ -102,6 +108,7 @@ const routes: Routes = [ LifecycleEventsComponent, ModuleRefComponent, LazyLoadingModulesComponent, + AdvancedComponent, ], }) export class FundamentalsModule {} From 5d3041816eae4f2187ce8612b0add45af65961b6 Mon Sep 17 00:00:00 2001 From: Jay McDoniel Date: Sat, 1 Oct 2022 18:35:36 -0700 Subject: [PATCH 2/4] fix: fix typescript capitalization on advanced page --- content/fundamentals/advanced.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/fundamentals/advanced.md b/content/fundamentals/advanced.md index 585c6e5717..a83ca96c2e 100644 --- a/content/fundamentals/advanced.md +++ b/content/fundamentals/advanced.md @@ -1,10 +1,10 @@ ### Advanced Concepts -So far we've breezed over a couple of advanced topics in Typescript like decorators and how the DI system is actually working under the hood. If all you want is a system that works, ou can probably move on to another section, but if you want to get a deeper understanding of how Nest works, this is the section for you. +So far we've breezed over a couple of advanced topics in TypeScript like decorators and how the DI system is actually working under the hood. If all you want is a system that works, ou can probably move on to another section, but if you want to get a deeper understanding of how Nest works, this is the section for you. #### The @Injectable() Decorator -Earlier in the docs we mentioned that "The `@Injectable()` decorator attaches metadata, which declares that `CatsService` is a class that can be managed by the Nest IoC container.". This is only a half truth, in reality, adding the provider to a module's `providers` array is what makes the provider able to be injected via the IoC container. What `@njectable()` is doing is forcing Typescript to emit metadata about the class's `constructor`, specifically the `design:paramtypes` metadata that will be read at start up. If there is an `@Inject()` decorator in the constructor, by technicality this does enough to make Typescript emit all the same metadata. Take a look at the compiled JavaScript from the following Typescript class: +Earlier in the docs we mentioned that "The `@Injectable()` decorator attaches metadata, which declares that `CatsService` is a class that can be managed by the Nest IoC container.". This is only a half truth, in reality, adding the provider to a module's `providers` array is what makes the provider able to be injected via the IoC container. What `@njectable()` is doing is forcing TypeScript to emit metadata about the class's `constructor`, specifically the `design:paramtypes` metadata that will be read at start up. If there is an `@Inject()` decorator in the constructor, by technicality this does enough to make TypeScript emit all the same metadata. Take a look at the compiled JavaScript from the following TypeScript class: ```typescript export class Foo { From 36e729075c0cfbddee40fc584613a997d6ca7029 Mon Sep 17 00:00:00 2001 From: Jay McDoniel Date: Mon, 23 Jan 2023 14:01:28 -0800 Subject: [PATCH 3/4] fix: adjust typos and grammar and update code style I started using Helix as an editor which doesn't directly support soft line wraps (it does now but it didn't) and got in the habit of keeping my markdown files to around 80 characters per line. This also leads to easier times in code reviews finding incorrect words and highlighting them due to only a single line being highlighted instead of an entire paragraph --- content/fundamentals/advanced.md | 36 ++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/content/fundamentals/advanced.md b/content/fundamentals/advanced.md index a83ca96c2e..ae2448d6f0 100644 --- a/content/fundamentals/advanced.md +++ b/content/fundamentals/advanced.md @@ -1,10 +1,23 @@ ### Advanced Concepts -So far we've breezed over a couple of advanced topics in TypeScript like decorators and how the DI system is actually working under the hood. If all you want is a system that works, ou can probably move on to another section, but if you want to get a deeper understanding of how Nest works, this is the section for you. +So far we've breezed over a couple of advanced topics in TypeScript like +decorators and how the DI system is actually working under the hood. +If all you want is a system that works, you can probably move on to another +section, but if you want to get a deeper understanding of how Nest works, this +is the section for you. #### The @Injectable() Decorator -Earlier in the docs we mentioned that "The `@Injectable()` decorator attaches metadata, which declares that `CatsService` is a class that can be managed by the Nest IoC container.". This is only a half truth, in reality, adding the provider to a module's `providers` array is what makes the provider able to be injected via the IoC container. What `@njectable()` is doing is forcing TypeScript to emit metadata about the class's `constructor`, specifically the `design:paramtypes` metadata that will be read at start up. If there is an `@Inject()` decorator in the constructor, by technicality this does enough to make TypeScript emit all the same metadata. Take a look at the compiled JavaScript from the following TypeScript class: +Earlier in the docs we mentioned that "The `@Injectable()` decorator attaches +metadata, which declares that `CatsService` is a class that can be managed by +the Nest IoC container.". This is only a half truth. In reality, adding the +provider to a module's `providers` array is what makes the provider able to be +injected via the IoC container. What `@Injectable()` is doing is forcing +TypeScript to emit metadata about the class's `constructor`, specifically the +`design:paramtypes` metadata that will be read at start up. If there is an +`@Inject()` decorator in the constructor, by technicality this does enough to +make TypeScript emit all the same metadata. Take a look at the compiled +JavaScript from the following TypeScript class: ```typescript export class Foo { @@ -41,7 +54,8 @@ export class FooBar { } ``` -Now, let's add the `@Injectable()` decorator to the classes. We get the following output in our `dist` +Now, let's add the `@Injectable()` decorator to the classes. We get the +following output in our `dist` ```javascript var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { @@ -86,8 +100,18 @@ FooBar = __decorate([ export { FooBar }; ``` -There's a lot going on in the above snippet, so let's ignore the `__decorate` and `__metadata` method definitions and look just at the class portions. Notice that now there are metadata definitions for the `design:paramtypes` metadata key of `[String]` for `Foo` and `Bar` and `[Foo, Bar]` for `FooBar`. This tells Nest what parameters are expected in each position of the constructor for each class. +There's a lot going on in the above snippet, so let's ignore the `__decorate` +and `__metadata` method definitions and look just at the class portions. +Notice that now there are metadata definitions for the `design:paramtypes` +metadata key of `[String]` for `Foo` and `Bar` and `[Foo, Bar]` for `FooBar`. +This tells Nest what parameters are expected in each position of the +constructor for each class. -> info **Hint** When you use `@Inject('SomeString')` Nest will set `design:paramtypes` to `SomeString` for the position that you are decorating. +> info **Hint** When you use `@Inject('SomeString')` Nest will set +`design:paramtypes` to `SomeString` for the position that you are decorating. -What Nest will do when reading this metadata is match it up with the class or injection token that exists in the current module's provider list and use that provider in the proper location for the constructor. This is why it is very important to either use a class, or `@Inject()` on the right parameter, and why all decorators cannot be used wherever you want. +What Nest will do when reading this metadata is match it up with the class or +injection token that exists in the current module's provider list and use that +provider in the proper location for the constructor. This is why it is very +important to either use a class, or `@Inject()` on the right parameter, and +why all decorators cannot be used wherever you want. From 3ce8f39696e4106c136b802dd884ba552bb56ce3 Mon Sep 17 00:00:00 2001 From: Jay McDoniel Date: Mon, 29 May 2023 13:08:44 -0700 Subject: [PATCH 4/4] fix: expand ioc first time it is used --- content/fundamentals/advanced.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/fundamentals/advanced.md b/content/fundamentals/advanced.md index ae2448d6f0..f9cc0a8c09 100644 --- a/content/fundamentals/advanced.md +++ b/content/fundamentals/advanced.md @@ -10,14 +10,14 @@ is the section for you. Earlier in the docs we mentioned that "The `@Injectable()` decorator attaches metadata, which declares that `CatsService` is a class that can be managed by -the Nest IoC container.". This is only a half truth. In reality, adding the -provider to a module's `providers` array is what makes the provider able to be -injected via the IoC container. What `@Injectable()` is doing is forcing -TypeScript to emit metadata about the class's `constructor`, specifically the -`design:paramtypes` metadata that will be read at start up. If there is an -`@Inject()` decorator in the constructor, by technicality this does enough to -make TypeScript emit all the same metadata. Take a look at the compiled -JavaScript from the following TypeScript class: +the Nest IoC (Inversion of Control) container.". This is only a half truth. In +reality, adding the provider to a module's `providers` array is what makes the +provider able to be injected via the IoC container. What `@Injectable()` is +doing is forcing TypeScript to emit metadata about the class's `constructor`, +specifically the `design:paramtypes` metadata that will be read at start up. If +there is an `@Inject()` decorator in the constructor, by technicality this does +enough to make TypeScript emit all the same metadata. Take a look at the +compiled JavaScript from the following TypeScript class: ```typescript export class Foo {