<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael D. Callaghan</title>
    <description>The latest articles on DEV Community by Michael D. Callaghan (@walkingriver).</description>
    <link>https://dev.to/walkingriver</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F138554%2F81670e72-360d-4cbd-9a74-07ce0b1bd200.jpg</url>
      <title>DEV Community: Michael D. Callaghan</title>
      <link>https://dev.to/walkingriver</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/walkingriver"/>
    <language>en</language>
    <item>
      <title>Upgrading Angular from 8 to 10 (Updated to 11)</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Thu, 21 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/angular/upgrading-angular-from-8-to-10-updated-to-11-11ik</link>
      <guid>https://dev.to/angular/upgrading-angular-from-8-to-10-updated-to-11-11ik</guid>
      <description>&lt;p&gt;Angular is on a six-month release cadence, which means you need to stay on top of them in your own projects. The last thing you want to do is wake up one day and find that the latest version was just released and you are stuck on a version from two and a half years ago. Fortunately, the Angular team has made it very easy to upgrade.&lt;/p&gt;

&lt;h1&gt;
  
  
  Official Upgrade Guidance
&lt;/h1&gt;

&lt;p&gt;Did you know that Angular offers official upgrade guidance? If you have not seen the website, take a look here at the &lt;a href="https://update.angular.io" rel="noopener noreferrer"&gt;Angular Upgrade Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  My Project
&lt;/h1&gt;

&lt;p&gt;The project I am upgrading is the demo application presented in my book and course on Ionic and Angular development. You can find &lt;a href="https://walkingriver.gumroad.com" rel="noopener noreferrer"&gt;both at Gumroad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The application itself is an Ionic app, written to run equally well on both desktop and mobile devices.&lt;/p&gt;

&lt;p&gt;At the time I began, it was an Angular 8 app that had been automatically generated by the Ionic CLI.&lt;/p&gt;

&lt;p&gt;You can see the app as it was just before I started the upgrade at its &lt;a href="https://github.com/walkingriver/at10dance-angular/tree/chapter2-demo" rel="noopener noreferrer"&gt;public GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I do not want it to fall too far behind, so I decided I should upgrade it to Angular 10 and make sure it works.&lt;/p&gt;

&lt;h1&gt;
  
  
  Angular 8.x to 8.y
&lt;/h1&gt;

&lt;p&gt;The first thing I did was make sure the project was on the latest version of Angular 8. I did this by first making sure I had a clean repo (I did) and then entering the following command (and its output):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng update @angular/cli@8 @angular/core@8

The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
Package '@angular/core' is already up to date.
    Updating package.json with dependency @angular-devkit/build-angular @ "0.803.29" (was "0.803.25")...
    Updating package.json with dependency @angular/cli @ "8.3.29" (was "8.3.25")...
UPDATE package.json (1619 bytes)
✔ Packages installed successfully.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I specifically asked the CLI to update to @angular/cli and @angular/core version 8, which will update to the most recent minor/revision available to that major version.&lt;/p&gt;

&lt;p&gt;I used &lt;code&gt;npx&lt;/code&gt; to use the local Angular CLI version in the project. As you can see from the warning that it had its own idea and installed a temporary version to perform the update.&lt;/p&gt;

&lt;p&gt;Apparently I was pretty close to current on Angular 8. It did not do much.&lt;/p&gt;

&lt;p&gt;Next, I committed the code. Angular will not update on an unclean working repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit -am "Updated to latest Angular 8"
[master e9e9c71] Updated to latest Angular 8
 2 files changed, 3429 insertions(+), 2214 deletions(-)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can compare what changed by &lt;a href="https://github.com/walkingriver/at10dance-angular/commit/e9e9c712a5f68933b690a70c55816ec75145dcc1" rel="noopener noreferrer"&gt;reviewing the diff on GitHub&lt;/a&gt;. Again, not much happened.&lt;/p&gt;

&lt;h1&gt;
  
  
  Angular 8 to Angular 9
&lt;/h1&gt;

&lt;p&gt;Next, I upgraded the project to Angular 9, with this command (it should look familiar).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng update @angular/cli@9 @angular/core@9

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command did a lot more than the last one because now we are looking at an update of a major version.&lt;/p&gt;

&lt;p&gt;Here is the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
    Updating package.json with dependency @angular-devkit/build-angular @ "0.901.12" (was "0.803.29")...
    Updating package.json with dependency @angular/cli @ "9.1.12" (was "8.3.29")...
    Updating package.json with dependency @angular/compiler @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency @angular/compiler-cli @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency @angular/language-service @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency typescript @ "3.8.3" (was "3.4.5")...
    Updating package.json with dependency @angular/common @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency @angular/core @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency @angular/forms @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency @angular/platform-browser @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency @angular/platform-browser-dynamic @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency @angular/router @ "9.1.12" (was "8.2.14")...
    Updating package.json with dependency zone.js @ "0.10.3" (was "0.9.1")...
UPDATE package.json (1620 bytes)
✔ Packages installed successfully.
**Executing migrations of package '@angular/cli'**

❯ Update an Angular CLI project to version 9.
UPDATE angular.json (5386 bytes)
  Migration completed.

**Executing migrations of package '@angular/core'**

❯ Static flag migration.
  Removes the `static` flag from dynamic queries.
  As of Angular 9, the "static" flag defaults to false and is no longer required for your view and content queries.
  Read more about this here: https://v9.angular.io/guide/migration-dynamic-flag
  Migration completed.

❯ Missing @Injectable and incomplete provider definition migration.
  In Angular 9, enforcement of @Injectable decorators for DI is a bit stricter and incomplete provider definitions behave differently.
  Read more about this here: https://v9.angular.io/guide/migration-injectable
  Migration completed.

❯ ModuleWithProviders migration.
  In Angular 9, the ModuleWithProviders type without a generic has been deprecated.
  This migration adds the generic where it is missing.
  Read more about this here: https://v9.angular.io/guide/migration-module-with-providers
  Migration completed.

❯ Renderer to Renderer2 migration.
  As of Angular 9, the Renderer class is no longer available.
  Renderer2 should be used instead.
  Read more about this here: https://v9.angular.io/guide/migration-renderer
  Migration completed.

❯ Undecorated classes with decorated fields migration.
  As of Angular 9, it is no longer supported to have Angular field decorators on a class that does not have an Angular decorator.
  Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes
  Migration completed.

❯ Undecorated classes with DI migration.
  As of Angular 9, it is no longer supported to use Angular DI on a class that does not have an Angular decorator.
  Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes
  Migration completed.

Your project has been updated to Angular version 9!
For more info, please see: https://v9.angular.io/guide/updating-to-version-9

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There were some warnings, which I will take care of eventually. For now, they did not affect the application. I then committed the changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit -am"Upgraded to Angular 9"
[master ba20fd7] Upgraded to Angular 9
 3 files changed, 3695 insertions(+), 2830 deletions(-)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/walkingriver/at10dance-angular/commit/ba20fd715c5e8373699e0c5d37aa638fe0f456b0" rel="noopener noreferrer"&gt;GitHub diff is here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The only thing of note that it changed was that it added this bit of JSON to the angular.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "type": "anyComponentStyle",
  "maximumWarning": "6kb"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Angular 9 to Angular 10
&lt;/h1&gt;

&lt;p&gt;Finally, I let the CLI upgrade from Angular 9 to Angular 10.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng update @angular/cli @angular/core              
The installed local Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
    Updating package.json with dependency @angular-devkit/build-angular @ "0.1001.7" (was "0.901.12")...
    Updating package.json with dependency @angular/cli @ "10.1.7" (was "9.1.12")...
    Updating package.json with dependency @angular/compiler @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency @angular/compiler-cli @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency @angular/language-service @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency typescript @ "4.0.3" (was "3.8.3")...
    Updating package.json with dependency @angular/common @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency @angular/core @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency @angular/forms @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency @angular/platform-browser @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency @angular/platform-browser-dynamic @ "10.1.6" (was "9.1.12")...
    Updating package.json with dependency @angular/router @ "10.1.6" (was "9.1.12")...
UPDATE package.json (1620 bytes)
✔ Packages installed successfully.
**Executing migrations of package '@angular/core'**

❯ Missing @Injectable and incomplete provider definition migration.
  As of Angular 9, enforcement of @Injectable decorators for DI is a bit stricter and incomplete provider definitions behave differently.
  Read more about this here: https://v9.angular.io/guide/migration-injectable
  Migration completed.

❯ ModuleWithProviders migration.
  As of Angular 10, the ModuleWithProviders type requires a generic.
  This migration adds the generic where it is missing.
  Read more about this here: https://v10.angular.io/guide/migration-module-with-providers
  Migration completed.

❯ Undecorated classes with Angular features migration.
  In version 10, classes that use Angular features and do not have an Angular decorator are no longer supported.
  Read more about this here: https://v10.angular.io/guide/migration-undecorated-classes
  Migration completed.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As before, I committed the code and checked the behavior of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit -am"Upgraded to Angular 10"
[master a0114c1] Upgraded to Angular 10
 2 files changed, 1945 insertions(+), 897 deletions(-)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/walkingriver/at10dance-angular/commit/a0114c149e637a90394b35a8888f83fac38728c8" rel="noopener noreferrer"&gt;GitHub diff is here&lt;/a&gt;. As you can see, it only updated packages. Though it showed many of the same warnings, it did not find anything in the code it needed to migrate.&lt;/p&gt;

&lt;p&gt;As I reviewed those migration guides, I also did not find any issues to update. I cannot confirm this, but I believe that the Ionic team keeps on top of these things and uses the latest Angular guidance in their application generators.&lt;/p&gt;

&lt;h1&gt;
  
  
  Upgrading to Angular 11
&lt;/h1&gt;

&lt;p&gt;A few months have gone by since I originally wrote this article. Angular 11.1 was just released, so I thought it would be a good idea to update both the article and the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular 10.2
&lt;/h2&gt;

&lt;p&gt;First, following the same pattern as before, I wanted to ensure I was on the latest version of Angular 10. I did that quickly and without error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng update @angular/cli@10 @angular/core@10
The installed local Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
    Updating package.json with dependency @angular-devkit/build-angular @ "0.1002.1" (was "0.1002.0")...
    Updating package.json with dependency @angular/cli @ "10.2.1" (was "10.2.0")...
    Updating package.json with dependency @angular/compiler @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/compiler-cli @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/language-service @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/common @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/core @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/forms @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/platform-browser @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/platform-browser-dynamic @ "10.2.4" (was "10.2.3")...
    Updating package.json with dependency @angular/router @ "10.2.4" (was "10.2.3")...
  UPDATE package.json (1620 bytes)
✔ Packages installed successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This made no code changes, which I did not really expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  TSLint and Codelyzer
&lt;/h2&gt;

&lt;p&gt;This is where I finally ran into some minor trouble. My initial migration to Angular 11 failed due to some incompatible peer dependencies with Codelyzer. &lt;/p&gt;

&lt;p&gt;Angular 11 finally does away with TSLint and Codelyzer for its linting tools, so I had to remove them before proceeding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm uninstall -D codelyzer tslint
git commit -am"Removed tslint and codelyzer"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This went smoothly, and simply removed those two entries from my package.json file.&lt;/p&gt;

&lt;p&gt;At that point, the &lt;code&gt;ng lint&lt;/code&gt; command not longer worked. Executing it resulted in this helpful error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TSLint's support is discontinued and we're deprecating its support in Angular CLI.
To opt-in using the community driven ESLint builder, see: https://github.com/angular-eslint/angular-eslint#migrating-from-codelyzer-and-tslint.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will leave that for another time, but it seems as though it will be reasonably straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular 11
&lt;/h2&gt;

&lt;p&gt;Now it was time to upgrade from Angular 10.2 to Angular 11.1. The command and its output are shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng update @angular/cli@11 @angular/core@11
The installed local Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 33 dependencies.
Fetching dependency metadata from registry...
    Updating package.json with dependency @angular-devkit/build-angular @ "0.1101.0" (was "0.1002.1")...
    Updating package.json with dependency @angular/cli @ "11.1.0" (was "10.2.1")...
    Updating package.json with dependency @angular/compiler @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency @angular/compiler-cli @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency @angular/language-service @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency karma @ "5.2.3" (was "4.1.0")...
    Updating package.json with dependency protractor @ "7.0.0" (was "5.4.3")...
    Updating package.json with dependency @angular/common @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency @angular/core @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency @angular/forms @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency @angular/platform-browser @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency @angular/platform-browser-dynamic @ "11.1.0" (was "10.2.4")...
    Updating package.json with dependency @angular/router @ "11.1.0" (was "10.2.4")...
  UPDATE package.json (1568 bytes)
✔ Packages installed successfully.
** Executing migrations of package '@angular/core' **

❯ In Angular version 11, the type of `AbstractControl.parent` can be `null` to reflect the runtime value more accurately.
  This migration automatically adds non-null assertions to existing accesses of the `parent` property on types like `FormControl`, `FormArray` and `FormGroup`.
  Migration completed.

❯ ViewEncapsulation.Native has been removed as of Angular version 11.
  This migration replaces any usages with ViewEncapsulation.ShadowDom.
  Migration completed.

❯ NavigationExtras omissions migration.
  In version 11, some unsupported properties were omitted from the `extras` parameter of the `Router.navigateByUrl` and `Router.createUrlTree` methods.
  Migration completed.

❯ Updates the `initialNavigation` property for `RouterModule.forRoot`.
  Migration completed.

❯ NavigationExtras.preserveQueryParams has been removed as of Angular version 11.
   This migration replaces any usages with the appropriate assignment of the queryParamsHandling key.
  Migration completed.

❯ The default value for `relativeLinkResolution` is changing from 'legacy' to 'corrected'.
This migration updates `RouterModule` configurations that use the default value to 
now specifically use 'legacy' to prevent breakages when updating.
  UPDATE src/app/app-routing.module.ts (782 bytes)
  Migration completed.

❯ `async` to `waitForAsync` migration.
  The `async` testing function has been renamed to `waitForAsync` to avoid confusion with the native `async` keyword.
  UPDATE src/app/app.component.spec.ts (2562 bytes)
  UPDATE src/app/home/home.page.spec.ts (647 bytes)
  UPDATE src/app/roster/roster.page.spec.ts (661 bytes)
  UPDATE src/app/student-info/student-info.page.spec.ts (697 bytes)
  Migration completed.

❯ Removes `canActivate` from a `Route` config when `redirectTo` is also present.
  Migration completed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there are some things going on here other than simply updating some libraries. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;async&lt;/code&gt; testing function has been renamed to &lt;code&gt;waitForAsync&lt;/code&gt; to avoid confusion with the native &lt;code&gt;async&lt;/code&gt; keyword. The migration tool converted them automatically.&lt;/li&gt;
&lt;li&gt;In my app's routing module (app-routing.module.ts), the migration added &lt;code&gt;relativeLinkResolution: 'legacy'&lt;/code&gt; to the &lt;code&gt;config:ExtraOptions&lt;/code&gt; parameter in the &lt;code&gt;RouterModule.forRoot&lt;/code&gt; call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As with the rest of the migrations, this one made no changes to the app's functionality, so I went ahead and committed the code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Will your upgrade go as smoothly? I cannot guarantee it, of course. Honestly, the application is modest enough that I did not expect any problems.&lt;/p&gt;

&lt;p&gt;As a convenient summary, here the commands I used (without the command output being shown).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng update @angular/cli@8 @angular/core@8
git commit -am "Updated to latest Angular 8"
npx ng update @angular/cli@9 @angular/core@9
npx ng generate component Loading --spec=false --dry-run\n
git commit -am"Upgraded to Angular 9"
npx ng update @angular/cli @angular/core
git commit -am"Upgraded to Angular 10"
npx ng update @angular/cli@10 @angular/core@10
git commit -am"Upgraded to Angular 10.2.4"
npm uninstall -D codelyzer tslint
npx ng update @angular/cli@11 @angular/core@11
git commit -am"Upgraded to Angular 11.1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I said, your experience may not be the same as mine. You may not even need &lt;code&gt;npx&lt;/code&gt;. I hope this article helped you, even a little, and I wish you every success in your own endeavors.&lt;/p&gt;

&lt;h1&gt;
  
  
  Discount Code
&lt;/h1&gt;

&lt;p&gt;If you are interested in either the book or the course on building web and mobile Apps with Angular and Ionic, here is a code good for 25% off the regular price.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Book: &lt;a href="https://gum.co/NlVUr/blog25" rel="noopener noreferrer"&gt;https://gum.co/NlVUr/blog25&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Course: &lt;a href="https://gum.co/FyZHi/blog25" rel="noopener noreferrer"&gt;https://gum.co/FyZHi/blog25&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Diagnosing Random Angular Test Failures</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/diagnosing-random-angular-test-failures-j8j</link>
      <guid>https://dev.to/walkingriver/diagnosing-random-angular-test-failures-j8j</guid>
      <description>&lt;p&gt;Have you ever had an intermittent or random failure in your unit tests? I did, and I was pulling my hair out trying to figure out the problem. Below I will describe how I finally managed to find the offending tests and solve the problem.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wGGKHGA_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/angular-jasmine-karma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wGGKHGA_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/angular-jasmine-karma.png" alt="Angular + Jasmine + Karma"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am using Angular 10 in this specific project, writing unit tests with Jasmine, and running them with Karma.&lt;/p&gt;

&lt;p&gt;My karma-jasmine configuration when I began was completely empty, meaning I was using the defaults. Not understanding those defaults caused much of my problem. Out of the box, your unit tests are run in a random order. I was vaguely aware of this, but it had completely slipped my mind at the time. If you take nothing else away from my story, remember this: Whenever you experience intermittent or random test failures, you can almost be sure that one test is causing another to break. The trouble is figuring out which combination.&lt;/p&gt;

&lt;h1&gt;
  
  
  After the First Failure
&lt;/h1&gt;

&lt;p&gt;As soon as I saw my first failure, I naively assumed it was the most recent test I added. Naturally, I removed it. At that point, the failure went away. I spent the next half hour trying to rewrite the "offending" test to figure out how it caused a failure in an unrelated test. The fact that the new test and the failing test were unrelated should have been my first clue that something else was at work here.&lt;/p&gt;

&lt;h1&gt;
  
  
  Start Disabling Tests
&lt;/h1&gt;

&lt;p&gt;As I write this, my project has 153 unit tests. The failure was occurring in an &lt;code&gt;afterAll&lt;/code&gt; function, which I did not even have. The error referred to a specific component, but not a specific test. I could not even determine which test to skip. Instead, I decided to start running a subset of tests by selectively disabling some of the other tests and test suites.&lt;/p&gt;

&lt;h1&gt;
  
  
  Disable Random Test Ordering
&lt;/h1&gt;

&lt;p&gt;By this point, I was reasonably sure the problem was one test causing a failure in another one. The trick now was to figure out which one. On a whim, I decided to turn off random ordering in the Jasmine configuration. As I said, I am running my tests with the Karma test runner. So, inside the karma.conf.js file, I simply needed to add some Jasmine-specific configuration to the &lt;code&gt;client&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;Before, that section looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;clearContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;// leave Jasmine Spec Runner output visible in browser&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To disable random test order, I added this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;clearContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// leave Jasmine Spec Runner output visible in browser&lt;/span&gt;
  &lt;span class="nx"&gt;jasmine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;random&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would guarantee that my tests would run "in order," whatever that means. It occurred to me that this probably would not reveal any new insights. I was right. All tests passed. The good news is that the test run was now repeatable. Every test run passed now.&lt;/p&gt;

&lt;p&gt;Did it solve my problem? Nope, but at least I felt I was making progress.&lt;/p&gt;

&lt;h1&gt;
  
  
  You Can Specify the Random Seed!
&lt;/h1&gt;

&lt;p&gt;The next thing I did was to turn random ordering back on, but this time provide my own random seed. If you are not familiar with a seed, it is a number used to initialize (or "seed") the random number generator. The benefit of this approach is that using the same value to seed the random number generator will provide the identical sequence of random values on subsequent runs. Thus, once I found a seed that caused my test failure to show itself, I could continue using that seed during my investigation.&lt;/p&gt;

&lt;p&gt;I started with the seed &lt;code&gt;1234&lt;/code&gt;. Because I had no idea what value might cause the problem, it really did not matter. On the first run with that value, all the tests passed, so that was no help. I continued changing the seed value until my test failed. Fortunately, it only took me a couple of attempts. I ended up with a configuration like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;clearContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// leave Jasmine Spec Runner output visible in browser&lt;/span&gt;
  &lt;span class="nx"&gt;jasmine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;random&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;12345&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Start Isolating Tests
&lt;/h1&gt;

&lt;p&gt;Now that I could reliably reproduce the problem, it was time to start isolating the failed test from the working ones.&lt;/p&gt;

&lt;p&gt;On my first attempt, I simply ran only the failing test in that test suite, by changing &lt;code&gt;describe&lt;/code&gt; to &lt;code&gt;fdescribe&lt;/code&gt;. This block had only one &lt;code&gt;it&lt;/code&gt; block, so I was only running a single test from this suite. The rest of the test suites continued to run as normal. I was not yet ready to turn them off.&lt;/p&gt;

&lt;p&gt;As I expected would happen, the test passed. When run in isolation from the other tests in the test suite, it worked just fine.&lt;/p&gt;

&lt;p&gt;As a sanity check, I disabled every other test in the project, and as expected, the remaining test passed.&lt;/p&gt;

&lt;p&gt;Next, I began turning on one test at a time. I considered turning on half, and then the other half. However, this particular suite only had ten tests in it, so I simply started at the top and turned them on one by one. As luck would have it, the very first test I enabled caused the problematic test to fail.&lt;/p&gt;

&lt;p&gt;Now the real investigation began. What code did the first test run that caused the second test to fail?&lt;/p&gt;

&lt;h1&gt;
  
  
  Give Asynchronous Code a Closer Look
&lt;/h1&gt;

&lt;p&gt;As I looked closer at the failing test, I noticed that it was testing a failure scenario. A passing test actually meant that an error occurred inside my component. This test used the &lt;code&gt;async/await&lt;/code&gt; pattern to wait on the asynchronous service call it had to make.&lt;/p&gt;

&lt;p&gt;I had seen this problem before, and almost certainly knew what had caused it. The function in many of my components that made asynchronous service calls were not returning the promise to the caller. Consider this block of code (not my actual code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Component function&lt;/span&gt;
&lt;span class="nx"&gt;getSomeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Service function&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code, the promise returned by &lt;code&gt;service.getData()&lt;/code&gt; is being returned by the component's &lt;code&gt;getSomeData()&lt;/code&gt; function to its caller. If you forget to do that, the testing code will have nothing to await. My component had many such functions, and I had been meticulously going through them all to make sure they returned the service call's promise. I immediately checked the particular function being exercised by the failing test. To my surprise, it was fine.&lt;/p&gt;

&lt;p&gt;That, however, led me to another realization. The test itself was bad. It looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set an error if it throws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// service here is a test double, a mock of the real service&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSomeData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The testing code was using &lt;code&gt;async/await&lt;/code&gt;, but had no &lt;code&gt;try/catch&lt;/code&gt;! I decided that I would rewrite the test without async/await, instead using &lt;code&gt;catch&lt;/code&gt;. The modified test looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set an error if it throws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// service here is a test double, a mock of the real service&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSomeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At last, I was pretty sure I had found the problem. I eagerly enabled all units tests and reran the tests with the same seed I had been using. The problematic test failed again, with the exact same error!&lt;/p&gt;

&lt;h1&gt;
  
  
  New Test Suite
&lt;/h1&gt;

&lt;p&gt;At this point, I decided to start with a clean slate. I disabled all tests in that component's test suite. Then I used the Angular CLI to create a brand new component, complete with its unit test boilerplate. &lt;/p&gt;

&lt;p&gt;Once that was done, I painstakingly began copying tests one at a time from the old test suite into the new one. I started simply, copying just enough code to initialize the component. That test failed due to missing service doubles. Next, I copied those test doubles (mostly service mocks) into the new suite. &lt;/p&gt;

&lt;p&gt;With the test doubles in place, my component creation test passed. One down, nine to go.&lt;/p&gt;

&lt;p&gt;The next test up was the failing one. It did something only other test in the suite does. It changed the behavior of one of my mock services. Could that be the piece I had been missing?&lt;/p&gt;

&lt;h1&gt;
  
  
  Suspect Your Test Doubles
&lt;/h1&gt;

&lt;p&gt;I deleted my test doubles and reentered them individually. While doing this, I noticed that two of my mock services were created as object literals directly in the test suite. This was odd, as I also had a mock service defined as a class in another file. I decided I should try to use that instead. &lt;/p&gt;

&lt;p&gt;The code used to look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This was provided to the component's testing module like this:&lt;/span&gt;
&lt;span class="nl"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I moved the &lt;code&gt;getData&lt;/code&gt; call into my Mock Service and changed the provider to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This was provided to the component's testing module like this:&lt;/span&gt;
&lt;span class="nl"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DataService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had left in the variable declaration so that I could redefine its behavior in the error path. But now I had a compiler error. This line in my failing test was no longer valid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is because service was now strongly-typed to be a DataService, and &lt;code&gt;getData&lt;/code&gt; returned a promise. It had no function named &lt;code&gt;rejects&lt;/code&gt;. The solution was to replace the &lt;code&gt;getData&lt;/code&gt; function completely, but just for this test.&lt;/p&gt;

&lt;p&gt;Recall that I said this test was inside its own &lt;code&gt;describe&lt;/code&gt; block, so I added a &lt;code&gt;beforeEach&lt;/code&gt; to it, where I could replace the behavior of the &lt;code&gt;getData&lt;/code&gt; function to reject instead of resolve the promise. Now my test looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getSomeData (error path)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Redefine service.getData here, outside the test itself.&lt;/span&gt;
  &lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set an error if it throws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// service here is a test double, a mock of the real service&lt;/span&gt;
  &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSomeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I am using &lt;code&gt;sinon&lt;/code&gt; to create the stub, but you can use a similar strategy with Jasmine's own functions. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;beforeEach&lt;/code&gt; on the overall test suite itself creates a brand new instance of the mock service before each test. Then, in this test, its &lt;code&gt;getData&lt;/code&gt; function is replaced with a new function that returns a rejected promise instead.&lt;/p&gt;

&lt;p&gt;I was feeling pretty good about where this was heading. I enabled all the tests and reran them. All passed. Finally, I removed the seed value from the Jasmine configuration and let the tests run in a random order. The test has been passing ever since.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;I have been careful not to show any of the actual test code that caused my problem, but instead just enough code to get my point across. My goal was to discuss how to isolate the problems in your tests, rather than discuss testing strategy. To that end, I hope I have succeeded.&lt;/p&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand that tests run in no particular order, and that is ideally what you want.&lt;/li&gt;
&lt;li&gt;Turn off random execution to help find the culprit.&lt;/li&gt;
&lt;li&gt;If sequential execution does not help, try various random "seed" values until the tests are failing consistently.&lt;/li&gt;
&lt;li&gt;Isolate the failing test or test suite by using &lt;code&gt;fdescribe&lt;/code&gt;, &lt;code&gt;fit&lt;/code&gt;, &lt;code&gt;xdescribe&lt;/code&gt;, and &lt;code&gt;xit&lt;/code&gt; to turn tests on or off selectively.&lt;/li&gt;
&lt;li&gt;Pay close attention to asynchronous code and test doubles.&lt;/li&gt;
&lt;li&gt;Ensure your test doubles are typed correctly, especially your mock services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this summary to be worthwhile and useful. If you have any tricks or tips of your own when it comes to diagnosing unit tests, please feel free to share them with me.&lt;/p&gt;

&lt;h1&gt;
  
  
  Angular Advocate
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EVjICqOW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/aa-3d-small.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EVjICqOW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/aa-3d-small.jpg" alt="Angular Advocate Book"&gt;&lt;/a&gt;&lt;br&gt;
If you are interested in more content like this, please consider my recently-released book, &lt;em&gt;Angular Advocate: How to Awaken the Champion Within and Become the Go-to Expert at Work&lt;/em&gt;, available in a &lt;a href="https://amzn.to/3p00l67"&gt;Kindle Edition at Amazon&lt;/a&gt; or &lt;a href="https://gum.co/angular-advocate"&gt;DRM-free on Gumroad&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>unittests</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What Did Prettier Do to My HTML?</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Wed, 06 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/what-did-prettier-do-to-my-html-1a9p</link>
      <guid>https://dev.to/walkingriver/what-did-prettier-do-to-my-html-1a9p</guid>
      <description>&lt;p&gt;I have resisted installing and using Prettier for a long time now, mostly because I was happy enough with the job VS Code does formatting my code. Then over the Christmas break, my son convinced me to install it. After I did, he said, “Now open an HTML file and I’ll show you something you aren’t going to like.” Sure, now he tells me! That struck me as ominous, but I decided to go along for the ride.&lt;/p&gt;

&lt;p&gt;We opened an HTML file and immediately reformatted the file using Prettier’s default settings. Near the top of the file was a block of HTML rendering an unordered list, representing a navigation menu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/" (click)="closeSidebarPanel()"&amp;gt;Home&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/reports/" (click)="closeSidebarPanel()"&amp;gt;Reports&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/annual-sales/" (click)="closeSidebarPanel()"&amp;gt;Annual Sales&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/product-maintenance/" (click)="closeSidebarPanel()"&amp;gt;Product Maintenance&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last two lines with &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags are longer than 80 characters, which is Prettier’s default maximum line length. During a reformat to wrap those lines, Prettier needs to make a decision on how to do so without semantically affecting the rendered output. The decision most formatters would make might end up with a block of code like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/" (click)="closeSidebarPanel()"&amp;gt;Home&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/reports/" (click)="closeSidebarPanel()"&amp;gt;Reports&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/annual-sales/" (click)="closeSidebarPanel()"&amp;gt;
    Annual Sales&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/product-maintenance/" (click)="closeSidebarPanel()"&amp;gt;
    Product Maintenance&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this is that the HTML &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag is an inline-display element, meaning it flows along with the text that’s around it. A code formatter has no way to know whether the whitespace it just added to the beginning of the line is significant. It is entirely possible, even likely, that it just inadvertently added an extra space to the menu label.&lt;/p&gt;

&lt;p&gt;What does Prettier do instead? It tries to make an intelligent decision on how to wrap and reformat those lines based on an item’s CSS display rules. If the element is a block-level element, then adding space around it will not affect its layout at all. On the other hand, if the element is an inline-display element, it will take care not to add any additional whitespace. What it does instead is rather clever. This is the final output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/" (click)="closeSidebarPanel()"&amp;gt;Home&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/reports/" (click)="closeSidebarPanel()"&amp;gt;Reports&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/annual-sales/" (click)="closeSidebarPanel()"
      &amp;gt;Annual Sales&amp;lt;/a
    &amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a routerLink="/product-maintenance/" (click)="closeSidebarPanel()"
      &amp;gt;Product Maintenance&amp;lt;/a
    &amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whitespace inside the tag itself, around its attributes, is never significant. Thus, it wraps the line between the final attribute and the angle bracket that closes the tag. That angle bracket is placed on the next line (remember, that whitespace is irrelevant), followed immediately by the text of the link itself. The link is then closed, again without any whitespace added to the text.&lt;/p&gt;

&lt;h1&gt;
  
  
  What about that closing angle bracket?
&lt;/h1&gt;

&lt;p&gt;Why does it do this, though?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;a routerLink="/product-maintenance/" (click)="closeSidebarPanel()"
      &amp;gt;Product Maintenance&amp;lt;/a
    &amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And not this, which might be more intuitive?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;a routerLink="/product-maintenance/" (click)="closeSidebarPanel()"
      &amp;gt;Product Maintenance&amp;lt;/a&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason, as I understand it, is that Prettier is intentionally placing the closing angle bracket on its own line, vertically lining it up with the tag’s opening angle bracket. In this way, it visually appears as a semantic block, exactly the way curly-brace languages line up their braces. Viewed from that perspective, I find it to be both elegant and clever.&lt;/p&gt;

&lt;p&gt;My son was right. I did not like it when I first saw it, but with understanding comes appreciation and acceptance.&lt;/p&gt;

&lt;h1&gt;
  
  
  HTML Attribute Organization
&lt;/h1&gt;

&lt;p&gt;Another change I did not expect is the way Prettier organizes HTML attributes when the tag has a lot of them. It formats them with one attribute per line, and closes the tag with the &lt;code&gt;/&amp;gt;&lt;/code&gt; on the final line, as it did with the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag above. I found a couple things to like about this.&lt;/p&gt;

&lt;p&gt;It is really easy to find, edit, add, and remove attributes. This is particularly noticeable if you use a source control system such as Git (you do use source control, right???).&lt;/p&gt;

&lt;p&gt;Consider the following HTML element definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input #emailAddress type="email" class="email-input" placeholder="Email Address" required id="email-address" title="Enter your email address" name="email-Address" [(ngModel)]="emailAddress"&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is quite the list of attributes, but it is by no means unusual. At first glance, is this element required? Does it have an &lt;code&gt;id&lt;/code&gt; attribute? Finding the answers to those question is not difficult, but it does take a little effort to scan the attributes when they are organized that way.&lt;/p&gt;

&lt;p&gt;This is how Prettier reformats that element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input
  #emailAddress
  type="email"
  class="email-input"
  placeholder="Email Address"
  required
  id="email-address"
  title="Enter your email address"
  name="email-Address"
  [(ngModel)]="emailAddress"
/&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that it did not change the order of the attributes. It honored my decision of where each belongs. Some formatters insist on alphabetizing them. That may even be a Prettier option, but as I said above, I am only working with its defaults for now. Maybe I will investigate and tweak it later, but one of its advantages is that you can use it “right out of the box,” as it were.&lt;/p&gt;

&lt;p&gt;The final piece of brilliance with this formatting decision concerns source control, which I mentioned earlier. In the first element definition above, if I were to add a new attribute (say, &lt;code&gt;disabled&lt;/code&gt;), the entire line would show as “changed” in the source control’s “diff” screen. Granted, finding such a small change would be reasonably simple and require very little mental effort.&lt;/p&gt;

&lt;p&gt;Now imagine many such changes. Each one by itself is simple, but adding dozens or hundreds of these changes can start to feel like a burden.&lt;/p&gt;

&lt;p&gt;Prettier’s decision of displaying one-attribute-per line means that each change affects exactly one line. Changes are easy to spot; they clearly stand out from the surrounding code, allowing you to see and understand what changed quickly and easily.&lt;/p&gt;

&lt;p&gt;This advantage carries through to adding attributes right before the closing &lt;code&gt;/&amp;gt;&lt;/code&gt; characters. Because a new attribute is on its own line, it will display as a single change in source control.&lt;/p&gt;

&lt;p&gt;To me, this is a huge win.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bottom Line
&lt;/h1&gt;

&lt;p&gt;I will keep using Prettier, at least for my own projects. Next is getting my team at work to share in my appreciation.&lt;/p&gt;

&lt;p&gt;The longer explanation of the whitespace decision can be found &lt;a href="https://prettier.io/blog/2018/11/07/1.15.0.html"&gt;on the Prettier blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>codeformatting</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Upgrading an AngularJS Project to Angular</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Wed, 30 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/upgrading-an-angularjs-project-to-angular-4blf</link>
      <guid>https://dev.to/walkingriver/upgrading-an-angularjs-project-to-angular-4blf</guid>
      <description>&lt;p&gt;For the past few months I have been involved with migrating an AngularJS 1.4 app to a more modern version of Angular. Below I will describe some of the processes, techniques, and issues I have encountered to make the migration successful.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preparation
&lt;/h1&gt;

&lt;p&gt;Before starting the migration, there are a few things that will make it easier, or harder, depending on your project’s configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript
&lt;/h2&gt;

&lt;p&gt;I was fortunate in that the entire AngularJS project I was migrating was already written in TypeScript. Each AngularJS controller was already a single class. If that had not been the case, I would still consider the controller itself to be a component in the new project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing
&lt;/h2&gt;

&lt;p&gt;My project used the Angular UI router. In your new project, I recommend using the default app routing module. Make a note of each state or route in your app. Copy them down with their relative URLs so that you don’t forget any.&lt;/p&gt;

&lt;h2&gt;
  
  
  ControllerAs or $scope pattern
&lt;/h2&gt;

&lt;p&gt;Your AngularJS project is probably built with one of two patterns: You either use &lt;code&gt;$scope&lt;/code&gt; to reference variables on your controller, or you created a “view-model” object and referred to it by name in your template. With an Angular component, you will use neither. When you migrate your HTML templates, you will remove all instances of the view-model object. If you used &lt;code&gt;$scope&lt;/code&gt;, you probably won’t have to do anything to bind your variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  UI Directives
&lt;/h2&gt;

&lt;p&gt;In one of my projects, all our UI directives were already written as components. In another, the one I am currently migrating, they are not. The good news is that UI components and directives migrate just as easily as pages. To Angular, they are all simply components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bower Packages
&lt;/h2&gt;

&lt;p&gt;Bower has all but been abandoned. Check your bower.json file and bower_components folder for any libraries you think you may need to keep. By now, most every bower package your old AngularJS project uses can be found in npm. Don’t bother trying to find new versions until you know you need them. What I found is that I could ignore them mostly, finding them in npm as necessary.&lt;/p&gt;

&lt;h1&gt;
  
  
  Start a new project
&lt;/h1&gt;

&lt;p&gt;Starting with the latest Angular CLI, the first thing I did was create a brand new project with &lt;code&gt;ng new&lt;/code&gt;. That provides a simple skeleton app, scaffolded with a single page and pre-configured routing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Migrate one page at a time
&lt;/h1&gt;

&lt;p&gt;To migrate a page, I used the Angular CLI to create a page in the new project, using the same name as the old page. For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate component ProductDetail

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Remember: All pages in Angular are components.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By default, this command creates four new files in a folder called product-detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;product-detail.component.html&lt;/li&gt;
&lt;li&gt;product-detail.component.ts&lt;/li&gt;
&lt;li&gt;product-detail.component.scss&lt;/li&gt;
&lt;li&gt;product-detail.component.spec.ts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It will also modify app.module.ts to reference your newly-created component.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about lazy-loaded page modules?
&lt;/h2&gt;

&lt;p&gt;The project I migrated does not use lazy-loaded pages, but I recommend that you do so if you can. If I have time, I may convert this project to do that, but it’s definitely out of scope for now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Copy the controller code into the component
&lt;/h1&gt;

&lt;p&gt;The first thing I do with any page is copy the controller code from the old page into the new page’s component class. An empty component class looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-product-detail",
  templateUrl: "./product-detail.component.html",
  styleUrls: ["./product-detail.component.scss"],
})
export class ProductDetailComponent implements OnInit {
  constructor() {}

  ngOnInit(): void {}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Variables
&lt;/h2&gt;

&lt;p&gt;First, find all all your variables in the old controller and copy them to top of the class, just above the constructor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Constructor and ngInject
&lt;/h2&gt;

&lt;p&gt;My controllers were already written in TypeScript, so I started with a class that had a constructor. Everything being “injected” into my controller are listed as parameters to the class constructor. Those got copied and pasted into the TypeScript component class without modification.&lt;/p&gt;

&lt;p&gt;If you are not using TypeScript in your AngularJS controller, you will still be able to find the list of items being injected into your controller by the array of dependencies being passed to your controller’s function parameters. Simply copy those to your new component’s constructor.&lt;/p&gt;

&lt;p&gt;The trick at this point is to provide the appropriate &lt;code&gt;import&lt;/code&gt; statement and types for each one. You may be able to put that off for a while, but eventually your component will need to know what those dependencies are. Each one will need to be matched to a type and an import at the top of the class file.&lt;/p&gt;

&lt;p&gt;In one case, my existing page listed eight such dependencies. Each one was either a bower package, an Angular service, or an internal service. Fortunately, I was able to find an npm equivalent for each bower package. The Angular services were not quite as simple, and I will touch on them later.&lt;/p&gt;

&lt;p&gt;The others were services internal to the project. For those, I needed to migrate or replace them. Because those are project-specific, I can only offer very generic advice.&lt;/p&gt;

&lt;p&gt;Try to use your internal services as-is, if you can. If you cannot, you may need to mock them temporarily to get your page to render.&lt;/p&gt;

&lt;p&gt;You will probably have more than one call to AngularJS’s $http service, which you will need to upgrade to Angular’s HttpClient. That is not terribly difficult, but you may want to mock those services so that you can focus on one task at a time.&lt;/p&gt;

&lt;p&gt;Another quirk is that all my older project’s constructor parameters were tagged as &lt;code&gt;public&lt;/code&gt;, which is not desirable in my opinion. When I copied them to the new component, I changed them to &lt;code&gt;private&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functions
&lt;/h2&gt;

&lt;p&gt;Next, copy every function from your controller into the blank space between &lt;code&gt;ngOnInit()&lt;/code&gt; and the final closing brace.&lt;/p&gt;

&lt;p&gt;If your controller contains any initialization logic, you may want to start with putting that into &lt;code&gt;ngOnInit()&lt;/code&gt;. Try not to put too much logic into your constructor.&lt;/p&gt;

&lt;p&gt;If you are fortunate enough to be copying TypeScript code, you won’t need to do much more. But if you are copying JavaScript, you will need to remove the word &lt;code&gt;function&lt;/code&gt; from each one of them. If your project has anonymous functions tied to controller variables, you may have a little extra work to do.&lt;/p&gt;

&lt;p&gt;For example, my controller functions all looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function doSomething() {
  ...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, all I had to do was remove the word &lt;code&gt;function&lt;/code&gt; and the rest of it could stay the same (for now).&lt;/p&gt;

&lt;p&gt;However, your controller functions might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var doSomething = function() {
  ...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In either case, my recommendation is that make all your functions look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;doSomething() {
  ...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dealing with this
&lt;/h2&gt;

&lt;p&gt;Once my functions were fixed, the next thing I discovered was that many of them had the following first line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var self = this;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rest of the function referred to the variable &lt;code&gt;self&lt;/code&gt; to read or write to variables on controller object itself. I won’t go into the joys of &lt;code&gt;this&lt;/code&gt; in JavaScript. You can find plenty of angry rants elsewhere for that. The good news is that this sort of thing simply isn’t necessary with TypeScript classes, which is what your new component is.&lt;/p&gt;

&lt;p&gt;So, to deal with that, the first thing I did was remove every line like the above. Then I converted all instances of &lt;code&gt;self.&lt;/code&gt; to &lt;code&gt;this.&lt;/code&gt; throughout the entire component. Yes, I used a blind find/replace, but so far it has never been an issue.&lt;/p&gt;

&lt;p&gt;I also found some places in my code that calls into other functions as a callback to another function, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.doCoolThingWithCallback(input, this.thingCallback.bind(this));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason for the &lt;code&gt;bind&lt;/code&gt; is so that &lt;code&gt;this&lt;/code&gt; is a reference to the caller inside &lt;code&gt;thingCallback&lt;/code&gt;. With TypeScript, simply replace that with an anonymous arrow function, which solves the “this” problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.doCoolThingWithCallback(input, () =&amp;gt; this.thingCallback());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Calling controller functions
&lt;/h2&gt;

&lt;p&gt;If you are following along, you may have some function calls flagged as errors because the functions are defined in your component. Simply prepend them with &lt;code&gt;this.&lt;/code&gt; and you should be fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anonymous functions
&lt;/h2&gt;

&lt;p&gt;The next thing I do is replace every anonymous function with an arrow function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service.getData()
  .then(function(data) {
    ...
  });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the TypeScript component, that simply becomes this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service.getData()
  .then((data) =&amp;gt; {
    ...
  });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Promises
&lt;/h1&gt;

&lt;p&gt;Many of my controllers use promises, and do so through the Bluebird library. TypeScript has built-in support for native Promises, so I have been able to remove Bluebird entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about async and await?
&lt;/h2&gt;

&lt;p&gt;If you are migrating older AngularJS to Angular with lots of promises, you might be tempted to convert them all to async and await. I did that at first. I strongly recommend you resist that temptation. At this point, your goal is not to refactor, but to migrate. You want to change as little code as possible. If you have promises that are working with &lt;code&gt;.then()&lt;/code&gt;, keep them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Navigation parameters
&lt;/h1&gt;

&lt;p&gt;My controller used &lt;code&gt;$stateParams&lt;/code&gt;, which map nicely to Angular’s ActivatedRoute. During the component’s &lt;code&gt;ngOnInit()&lt;/code&gt; function, I grab the snapshot from the ActivatedRoute and set the state parameters my component is already expecting.&lt;/p&gt;

&lt;p&gt;For example, one component was looking for the following, injected into its original controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $stateParams: { storeId: string, subsetId: string };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I moved that definition out of the constructor and into the component itself as a variable. Then, I modified &lt;code&gt;ngOnInit&lt;/code&gt; to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ngOnInit() {
    const snapshot = this.activatedRoute.snapshot;
    this.stateParams.storeId = snapshot.params.storeId;
    this.stateParams.subsetId = snapshot.params.subsetId;
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can also see, I also removed the &lt;code&gt;$&lt;/code&gt; from the variable name, which I did safely using the variable refactor tooling in my code editor.&lt;/p&gt;

&lt;h1&gt;
  
  
  Service refactorings
&lt;/h1&gt;

&lt;p&gt;As I mentioned above, my page had some external dependencies injected into it. Those needed to be addressed. I still had some older AngularJS constructs being injected that I needed to fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  $q
&lt;/h2&gt;

&lt;p&gt;The first was that &lt;code&gt;$q: ng.IQService&lt;/code&gt; was referenced. For that, I can simply remove it entirely and change anywhere it’s being used into a native TypeScript &lt;code&gt;promise&lt;/code&gt;. For example, I had this use of &lt;code&gt;$q&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.$q.all(promises).then(
  (data) =&amp;gt; {
    this.getAllProductsSuccess(data);
  },
  (data) =&amp;gt; {
    this.getAllProductsFailure(data);
  }
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I replaced it with this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Promise.all(promises).then(
  (data) =&amp;gt; {
    this.getAllProductsSuccess(data);
  },
  (data) =&amp;gt; {
    this.getAllProductsFailure(data);
  }
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case it is not clear, the variable &lt;code&gt;promises&lt;/code&gt; is defined as &lt;code&gt;Promise&amp;lt;any&amp;gt;[]&lt;/code&gt;. I will eventually do something about the &lt;code&gt;&amp;lt;any&amp;gt;&lt;/code&gt;, but for now it should be fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  $location
&lt;/h2&gt;

&lt;p&gt;The old AngularJS LocationService is used in my controller, but I’m not entirely sure why. In the case of the page I’m currently migrating, it was better to use the router, which is what I did instead. I found this function in the old code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  navigateToListing()
    this.$location.path('/listing');
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes no sense to me, as I would prefer to use the router. So, I changed the constructor to get a reference to the Angular Router object with &lt;code&gt;private router: Router&lt;/code&gt;, and changed the function to look like this instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  navigateToListing()
    this.router.navigateByUrl('/listing');
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the only call to this function was from a button in the HTML, I could also use a &lt;code&gt;[routerLink]&lt;/code&gt; attribute instead and remove the function call entirely, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button [routerLink]="/listing"&amp;gt;Return to Listings&amp;lt;/button&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  FormController
&lt;/h2&gt;

&lt;p&gt;If the page you are migrating has a FormController, as did mine, you may have a little more work to do. I had never used this before, but this page has a pretty complex form, so I think I understand why they used it initially.&lt;/p&gt;

&lt;p&gt;The definition at the official AngularJS docs says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FormController keeps track of all its controls and nested forms as well as the state of them, such as being valid/invalid or dirty/pristine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sounds like Angular’s Reactive Forms, so I immediately wondered whether I could replace the form with that. Peeking inside my template, I found many uses of &lt;code&gt;ng-model&lt;/code&gt;, which performs two-way data binding from the form to the controller. That patterns sounds like Angular’s Template-driven forms, so it required additional investigation.&lt;/p&gt;

&lt;p&gt;The service was injected into my controller as &lt;code&gt;form: IFormController&lt;/code&gt;. So, the first thing I wanted to do is find out how much it is used, which I did at the command line with grep.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; grep 'this.form' src/app/features/product-subset-detail/product-subset-detail.component.ts 
    this.form.$setPristine();
    this.form.$setPristine();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, that doesn’t seem too bad. For now, I decided simply to delete the reference and comment those two lines. Had it been more involved, I would have looked into refactoring the form into a ReactiveForm. But, as I said earlier, you want to avoid heavy refactoring until after you get the page migrating and functioning at least at a basic level.&lt;/p&gt;

&lt;h1&gt;
  
  
  Interfaces
&lt;/h1&gt;

&lt;p&gt;If your AngularJS project is written with JavaScript, you won’t have any interfaces. My project was in TypeScript, and had interfaces defined all over the place. During the migration process, I created a new folder just for them and copied each interface into its own file. This was absolutely unnecessary, but it cleaned up the code just a bit and made me happy.&lt;/p&gt;

&lt;h1&gt;
  
  
  The template
&lt;/h1&gt;

&lt;p&gt;With the controller migrated to a new component, it was time to turn my attention to the HTML template. My component code was free of compiler errors. Whether or not it works will still depend on whether or not I missed anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  ViewModel or $scope
&lt;/h2&gt;

&lt;p&gt;If your controller uses &lt;code&gt;$scope&lt;/code&gt;, your data bindings are probably already correctly mapped to your view. If your controller uses a viewmodel pattern, as mine all do, you need to get rid of that reference everywhere it exists in your template.&lt;/p&gt;

&lt;p&gt;For example, my controllers all used a viewmodel object named for the page (rather than simply &lt;code&gt;vm&lt;/code&gt; as I’ve seen many developers use). My data bindings all look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span&amp;gt;Product Name: &amp;lt;/span&amp;gt;`

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quick solution was to use Find/Replace to remove all occurrences of &lt;code&gt;ProductDetailVm.&lt;/code&gt; (don’t forget the dot). After that, the above data binding looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span&amp;gt;Product Name: &amp;lt;/span&amp;gt;`

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming I didn’t miss anything, the component should already have a property named &lt;code&gt;productName&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom directives
&lt;/h2&gt;

&lt;p&gt;At the very top of my first HTML template I found two separate custom directives. Those will obviously need to be dealt with at some point, but for now I chose to skip them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular directives
&lt;/h2&gt;

&lt;p&gt;Angular directives are much simpler to convert, so I decided to start there. Most of these can be handled with a simple find/replace operation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AngularJS&lt;/th&gt;
&lt;th&gt;Angular&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ui-sref&lt;/td&gt;
&lt;td&gt;[routerLink]&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-if&lt;/td&gt;
&lt;td&gt;*ngIf&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-show&lt;/td&gt;
&lt;td&gt;*ngIf&lt;/td&gt;
&lt;td&gt;It might make more sense to use &lt;code&gt;*ngClass{hidden: condition}&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-hide&lt;/td&gt;
&lt;td&gt;*ngIf&lt;/td&gt;
&lt;td&gt;It might make more sense to use &lt;code&gt;*ngClass{hidden: condition}&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-repeat&lt;/td&gt;
&lt;td&gt;*ngFor&lt;/td&gt;
&lt;td&gt;Requires additional syntax changes, see below.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-model&lt;/td&gt;
&lt;td&gt;[(ngModel)]&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-class&lt;/td&gt;
&lt;td&gt;ngClass&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-click&lt;/td&gt;
&lt;td&gt;(click)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-change&lt;/td&gt;
&lt;td&gt;(change)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-disabled&lt;/td&gt;
&lt;td&gt;[disabled]&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-pattern&lt;/td&gt;
&lt;td&gt;pattern&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ng-maxlength&lt;/td&gt;
&lt;td&gt;maxlength&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Granted, all of these will need to be revisited at some point to ensure that they do the right thing. There are a few extra steps to be taken once the attributes themselves were changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  ng-repeat and *ngFor
&lt;/h3&gt;

&lt;p&gt;I had a data table, where each table row &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; is repeated using &lt;code&gt;ng-repeat&lt;/code&gt;. This construct needed to be migrated to use &lt;code&gt;*ngFor&lt;/code&gt; with its modern syntax. It isn’t hard, but it’s also not a simple Find/Replace as many of these have been.&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;tr ng-repeat="item in displayedCollection"&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;tr *ngFor="let item of displayedCollection"&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I replaced &lt;code&gt;ng-repeat&lt;/code&gt; with &lt;code&gt;*ngFor&lt;/code&gt; and fixed the looping expression.&lt;/p&gt;

&lt;h3&gt;
  
  
  ngIf “gotchas”
&lt;/h3&gt;

&lt;p&gt;Remember that &lt;code&gt;*ngIf&lt;/code&gt; literally adds or removes elements from your page’s DOM. This is important if you ever try to get a reference to an element from your component.&lt;/p&gt;

&lt;p&gt;For example, I found code in my old controller that manipulated an HTML element directly. It called &lt;code&gt;document.getElementById&lt;/code&gt; to retrieve a reference to that element. I prefer to use Angular’s &lt;code&gt;@ViewChild&lt;/code&gt; decorator, as I find it to be a little cleaner. The “gotcha” is that if the element being referenced by &lt;code&gt;@ViewChild&lt;/code&gt; happens to be hidden inside another element that has an &lt;code&gt;*ngIf&lt;/code&gt;, it may not exist when you want to use it.&lt;/p&gt;

&lt;p&gt;For this reason, I prefer to keep my use of &lt;code&gt;*ngIf&lt;/code&gt; limited to very small elements, or not use it at all. Instead, I prefer to show/hide elements with a CSS &lt;code&gt;hidden&lt;/code&gt; class, which is simply defined as &lt;code&gt;.hidden { display:none; }&lt;/code&gt; in my app’s global style sheet. I find for most use cases, this works as well, if not better, than &lt;code&gt;*ngIf&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom components
&lt;/h2&gt;

&lt;p&gt;My form contained a custom DatePicker component that does not work with Angular. Fortunately I was able to find a replacement that did, which did not require too much additional customization.&lt;/p&gt;

&lt;p&gt;I recommend that as you convert your pages, try to determine ahead of time whether or not you will be using a third-party component library (such as Ionic, Angular Material, Bootstrap, etc.). It might be easier if you take inventory of all the custom components in your existing application, and then decide how to replace them in the migrated app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrap Grid???
&lt;/h2&gt;

&lt;p&gt;Speaking of Bootstrap, my AngularJS app makes heavy use of Bootstrap’s grid system. At first I thought I would simply remove and it replace it with something else. The trouble was I did not quite know what that “something else” would be. I briefly considered using Ionic’s &lt;code&gt;&amp;lt;ion-grid&amp;gt;&lt;/code&gt;, as it is quite straightforward to use Ionic components selectively. The unused portions get “tree-shaken” out at build time.&lt;/p&gt;

&lt;p&gt;I also considered downloading a &lt;a href="https://getbootstrap.com/docs/3.4/customize/"&gt;customized version of Bootstrap 3.4&lt;/a&gt;, including only the grid system.&lt;/p&gt;

&lt;p&gt;Then I stumbled on &lt;a href="https://speckyboy.com/replicate-bootstrap-grid-using-css-grid/"&gt;a blog post by Chris Wachtman on replicating the Bootstrap grid system with CSS Grid&lt;/a&gt;. The code looks pretty clean, so I’m going to give it a try.&lt;/p&gt;

&lt;h1&gt;
  
  
  Unit Testing
&lt;/h1&gt;

&lt;p&gt;I still need to migrate all my unit tests. You do have unit tests in your app, right? I certainly do, and many of them will need some love. One anti-pattern I uncovered during this migration is that many of the functions that make http calls don’t return the promise returned from the service.&lt;/p&gt;

&lt;p&gt;For example, consider this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  getProduct(): void {
    this.loading = true;
    this.myService.getProduct(
      this.productId
    ).toPromise()
      .then(
        (data) =&amp;gt; {
          this.getProductSuccess(data);
        },
        (data) =&amp;gt; {
          this.getProductFailure(data);
        }
      );
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing you may notice is the call to &lt;code&gt;.toPromise()&lt;/code&gt;. The call to &lt;code&gt;myService.getProduct()&lt;/code&gt; returns an observable. When I migrated all my services, I decided to &lt;a href="https://dev.to/walkingriver/to-rxjs-or-not-to-rxjs-4ao6"&gt;embrace RxJS as much as possible&lt;/a&gt;. However, for the purposes of migrating the individual pages, it was simpler to leave the promise handlers in place, at least for now. This function is essentially identical to the original from the AngularJS project, with the simple addition of the call to &lt;code&gt;.toPromise()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every one of my service calls follows this same pattern.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call the service&lt;/li&gt;
&lt;li&gt;Handle the success&lt;/li&gt;
&lt;li&gt;Or handle the failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you spotted the problem yet? There are two remaining.&lt;/p&gt;

&lt;p&gt;The first is that there is no &lt;code&gt;finally()&lt;/code&gt; call to reset &lt;code&gt;this.loading&lt;/code&gt;, which controls a visible loading indicator. That is handled in the both the success and failure functions. That’s minor, however, to the glaring problem preventing me from testing these functions property.&lt;/p&gt;

&lt;p&gt;The promise returned from &lt;code&gt;getProduct(...).toPromise()&lt;/code&gt; is never returned! This makes testing the function extremely difficult. Fortunately, simply adding a return statement to the front of it fixes it, and has no negative side-effects.&lt;/p&gt;

&lt;p&gt;This is the current implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  getProduct(): void {
    this.loading = true;

    return this.myService.getProduct(
      this.productId
    ).toPromise()
      .then(
        (data) =&amp;gt; {
          this.getProductSuccess(data);
        },
        (data) =&amp;gt; {
          this.getProductFailure(data);
        }
      ).finally(() =&amp;gt; {
        this.loading = false;
      });
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I still need to revisit and possibly rewrite every existing unit test, but that will be a topic for another time.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Next?
&lt;/h1&gt;

&lt;p&gt;At this point, everything was migrated and it was time to fire it up to see how it looks. As you might guess, it still isn’t perfect. However, it seems to be mostly functional.&lt;/p&gt;

&lt;p&gt;If you find any hints or tricks that work for you, which I did not address here, please let me know.&lt;/p&gt;

&lt;p&gt;I hope that your migration goes well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Angular Advocate
&lt;/h1&gt;

&lt;p&gt;Did you like this article? If so, please consider reading &lt;a href="https://gum.co/angular-advocate"&gt;Angular Advocate&lt;/a&gt;, my book on how you can become the go-to Angular expert at your company.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>New Book Announcement - Angular Advocate</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Mon, 30 Nov 2020 15:30:58 +0000</pubDate>
      <link>https://dev.to/walkingriver/new-book-announcement-angular-advocate-38h8</link>
      <guid>https://dev.to/walkingriver/new-book-announcement-angular-advocate-38h8</guid>
      <description>&lt;p&gt;How I became the go-to Angular Advocate at work – and how you can, too!&lt;/p&gt;

&lt;p&gt;In late spring 2018, my manager approached me with some interesting news. He explained that our executive director had complained that although Angular was prevalent throughout his organization, his own developers were not very experienced with it.&lt;/p&gt;

&lt;p&gt;What could be done about that? They discussed various options, from video course subscriptions, to books, to hiring an outside training firm to provide the necessary training.&lt;/p&gt;

&lt;p&gt;At that point, my manager spoke up. “What if we ask Mike Callaghan to do it?” He then proceeded to recommend me in an executive meeting to train a group of our software developers how to use Angular.&lt;/p&gt;

&lt;p&gt;Before I knew it, I was the Angular Advocate.&lt;/p&gt;

&lt;h1&gt;
  
  
  My Angular Journey
&lt;/h1&gt;

&lt;p&gt;I’ve been using Angular since AngularJS 1.2 or so. My first ever Angular app was a mobile swimming coach, implemented with the Ionic Framework and AngularJS.&lt;/p&gt;

&lt;p&gt;Today I build web applications with Angular almost exclusively, though I admit to having dabbled briefly with React, even so far as to writing &lt;a href="https://gum.co/ionic-react"&gt;a book on using React with Ionic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I start a new application, either personally or professionally, I tend to start with Angular.&lt;/p&gt;

&lt;h1&gt;
  
  
  What’s in this book?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B8mysF72--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/angular-advocate-3d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B8mysF72--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/angular-advocate-3d.jpg" alt="Angular Advocate Book"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This book consists of a series of essays I have written for various technical audiences over the past year or so. They are either directly or indirectly related to Angular development.&lt;/p&gt;

&lt;p&gt;However, this is not a book for web development beginners. There are plenty of those. Instead, this book is designed to appeal to experienced web developers with some exposure or experience with Angular.&lt;/p&gt;

&lt;p&gt;The content you will find is, admittedly, opinionated, as is the entire Angular ecosystem.&lt;/p&gt;

&lt;p&gt;I describe solutions that worked for me and my team. There are probably other approaches and solutions that would also work, but I will not dwell on them.&lt;/p&gt;

&lt;p&gt;The content of this book represents my experience, and I hope you will be able to learn something from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Topics Covered
&lt;/h2&gt;

&lt;p&gt;Here are some of the topics that are covered in the book. Notice it is not all simply Angular. There are other useful subjects that the Angular Advocate needs to understand.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to introduce coworkers to TypeScript.&lt;/li&gt;
&lt;li&gt;How to provide coworkers a Gentle introduction to Angular.&lt;/li&gt;
&lt;li&gt;How to create a simple “no-code” Angular component.&lt;/li&gt;
&lt;li&gt;How to display “offline” status in an Angular app.&lt;/li&gt;
&lt;li&gt;How to support [(ngModel)] in a custom Angular component.&lt;/li&gt;
&lt;li&gt;When to use route resolvers, and when not to use them.&lt;/li&gt;
&lt;li&gt;How to keep up with Angular releases. Fortunately, @Angular makes it easy to do.&lt;/li&gt;
&lt;li&gt;Understanding service APIs from both the producer and consumer point of view.&lt;/li&gt;
&lt;li&gt;Keeping your deployment options open.&lt;/li&gt;
&lt;li&gt;How to spin up a test API quickly and easily with Firebase.&lt;/li&gt;
&lt;li&gt;The benefits of Progressive Web Apps (PWAs) and when to use them.&lt;/li&gt;
&lt;li&gt;How to embrace RxJS and become truly effective.&lt;/li&gt;
&lt;li&gt;How to use Test-driven-development (TDD) effectively with Angular.&lt;/li&gt;
&lt;li&gt;Knowing what code to test (and what code not to test).&lt;/li&gt;
&lt;li&gt;Knowing what all those weird symbols on the command line are all about.&lt;/li&gt;
&lt;li&gt;How to revert your (or your team’s) mistaken git commits.&lt;/li&gt;
&lt;li&gt;How to provide source code without the overhead of the git repository.&lt;/li&gt;
&lt;li&gt;Understanding how people learn, before you need to teach.&lt;/li&gt;
&lt;li&gt;Knowing the basics of how to teach is critical on the path.&lt;/li&gt;
&lt;li&gt;How to get people to pay more attention when you speak.&lt;/li&gt;
&lt;li&gt;Why you should pay attention to spelling and grammar.&lt;/li&gt;
&lt;li&gt;Understanding the basics of agile planning.&lt;/li&gt;
&lt;li&gt;Where to get Creative-Commons licensed images for almost any purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How to Reserve Your Copy
&lt;/h1&gt;

&lt;p&gt;If these things sound interesting, and I hope they do, please consider &lt;a href="https://gum.co/angular-advocate"&gt;placing an order today at AngularAdvocate.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I am also releasing the first three chapters, free of charge. &lt;a href="https://gum.co/angular-advocate-preview"&gt;You can download the free PDF today&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Bizzare eBook Giveaway</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Thu, 05 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/a-bizzare-ebook-giveaway-2o1j</link>
      <guid>https://dev.to/walkingriver/a-bizzare-ebook-giveaway-2o1j</guid>
      <description>&lt;p&gt;Anyone who buys any one of my eBooks on Gumroad during November will automatically be entered into a drawing to win a Nook Color from Barnes &amp;amp; Noble preloaded with a copy of every title I have released in 2020.&lt;/p&gt;

&lt;h1&gt;
  
  
  This is the most bizarre giveaway I could think of
&lt;/h1&gt;

&lt;p&gt;Here is how it will work. During the month of November, all you need to do is buy any of my eBooks at Gumroad. At the end of the month, I will select one person at random and ship them a Nook Color tablet with a copy of each all three books I have released in 2020.&lt;/p&gt;

&lt;p&gt;There will also be some “runner up” prizes (see below).&lt;/p&gt;

&lt;h1&gt;
  
  
  The Books
&lt;/h1&gt;

&lt;p&gt;Here are the books in question, along with a link to purchase each of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t Say That at Work: Lessons Learned from a Lifetime of Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---NHxT_BZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/dst-3d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---NHxT_BZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/dst-3d.jpg" alt="Don't Say That at Work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever been told that your communication style is “too confrontational?” Do you have problems persuading your coworkers and managers without sounding arrogant or condescending? Have you said something and immediately regretted it? Have you ever said something you can’t take back? Have you ever wondered later, ‘what was I thinking???’&lt;/p&gt;

&lt;p&gt;We have all been there. We all make mistakes. We all suffer from a lapse in good judgment from time to time. Sometimes these mistakes are a “one off” but when made repeatedly can lead to a domino effect of problems. When that happens, it can be hard to recover.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://walkingriver.gumroad.com/#ZONxF"&gt;Order Here: Don’t Say That at Work: Lessons Learned from a Lifetime of Mistakes&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing Mobile Apps with Ionic and Angular
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tycobW2y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/ia-3d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tycobW2y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/ia-3d.jpg" alt="Don't Say That at Work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn to Build Mobile Apps! The Ionic Framework makes it easy&lt;/p&gt;

&lt;p&gt;The Ionic Framework supports a variety of mobile platforms. Throughout this book, I will cover the important aspects of development with Ionic and Angular, going from the initial idea all the way to the Apple App and Google Play Stores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://walkingriver.gumroad.com/#NlVUr"&gt;Order Here: Developing Mobile Apps with Ionic and Angular&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing Mobile Apps with Ionic and React
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2SisXLn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/ir-3d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2SisXLn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/ir-3d.jpg" alt="Don't Say That at Work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn to Build Mobile Apps! The Ionic Framework makes it easy&lt;/p&gt;

&lt;p&gt;The Ionic Framework supports a variety of mobile platforms. Throughout this book, I will cover the important aspects of development with Ionic and React, going from the initial idea all the way to the Apple App and Google Play Stores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://walkingriver.gumroad.com/#NlVUr"&gt;Order Here: Developing Mobile Apps with Ionic and React&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus - Angular Advocate (Unreleased)
&lt;/h2&gt;

&lt;p&gt;As an added bonus, the winner will also receive an advanced preview copy of my upcoming book, &lt;em&gt;Angular Advocate: How to Awaken the Champion Within and Become the Go-to Expert at Work&lt;/em&gt; (Working Draft Title).&lt;/p&gt;

&lt;h1&gt;
  
  
  A Nook Color?
&lt;/h1&gt;

&lt;p&gt;Why am I giving away the books on a Nook Color? Admittedly it’s a novelty. I was recently going through my old electronics and found my Nook Color from about 10 years ago. I charged it and turned it on. It still works! So, rather than throw it away or recycle it, I thought it would be fun to load my eBook titles on it and give it away.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YjBAOKYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/nook.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YjBAOKYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/nook.jpg" alt="Nook Color"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  No seriously? A Nook Color?
&lt;/h1&gt;

&lt;p&gt;Sure, why not? Let’s face it, you aren’t really winning the Nook, but the titles loaded on it. I just thought it would be amusing to put the books on the device and award them that way. If you win and really don’t want the Nook, just let me know. I’ll understand, honest.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prizes
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;1 Grand Prize: The Nook Color with all four of my books (including the unreleased one).&lt;/li&gt;
&lt;li&gt;5 1st Prizes: All three of my current books.&lt;/li&gt;
&lt;li&gt;10 2nd Prizes: Any one of my current books of your choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What’s the fine print?
&lt;/h1&gt;

&lt;p&gt;I will only ship the Nook to an address in the United States. If the Grand Prize winner is from outside the US, the winner will automatically receive one of my 1st prizes, and then I will select an additional winner.&lt;/p&gt;

&lt;p&gt;If you want to enter the giveaway without purchasing anything at all, that’s fine, too. Simply &lt;a href="https://twitter.com/walkingriver"&gt;follow me on twitter (@WalkingRiver)&lt;/a&gt; and tweet a link to this post, including the hashtag #BizarreGiveaway. I’ll make sure you’re entered.&lt;/p&gt;

</description>
      <category>ebook</category>
      <category>giveaway</category>
      <category>sweepstakes</category>
    </item>
    <item>
      <title>A Simple Angular Component</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Tue, 03 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/a-simple-angular-component-25ao</link>
      <guid>https://dev.to/walkingriver/a-simple-angular-component-25ao</guid>
      <description>&lt;p&gt;Note: this is an excerpt from my upcoming book, &lt;a href="https://gum.co/angular-advocate"&gt;Angular Advocate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Angular components do not need to be complicated. In fact, with a little HTML and CSS, it is reasonably straightforward to build a component you can reuse in all your projects. I will detail the creation of a “Loading” indicator.&lt;/p&gt;

&lt;p&gt;On one of my projects I needed to display a small screen that simply tells the user that data is being loaded from a remote service. Angular makes this almost too easy.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create the Component
&lt;/h1&gt;

&lt;p&gt;To create the component, I used the Angular CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng generate component Loading --spec=false --dry-run

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is essentially the same with either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE src/app/loading/loading.component.scss (0 bytes)
CREATE src/app/loading/loading.component.html (26 bytes)
CREATE src/app/loading/loading.component.ts (272 bytes)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command asks the Angular CLI to generate anew component named “Loading”, not to bother generating a test file (I will explain why not shortly) and then simply show me what the command will do (–dry-run).&lt;/p&gt;

&lt;p&gt;I almost always do a dry run before having the CLI generate anything for me. That way, I can what files it will create and modify and where it will put them. On some projects I like to organize components differently than the default. Seeing the file paths before creation gives me a chance to correct them, simply by pre-pending the path to the name of the component.&lt;/p&gt;

&lt;p&gt;In this case, I am comfortable with the component living in its own folder under &lt;code&gt;app&lt;/code&gt;, so I can rerun the command without the &lt;code&gt;--dry-run&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx ng generate component Loading --spec=false          

CREATE src/app/loading/loading.component.scss (0 bytes)
CREATE src/app/loading/loading.component.html (26 bytes)
CREATE src/app/loading/loading.component.ts (272 bytes)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A note about the &lt;code&gt;npx&lt;/code&gt; prefix: I need to add this to the command because my &lt;code&gt;ng&lt;/code&gt; is not installed globally. Using &lt;code&gt;npx&lt;/code&gt; causes the Angular CLI installed in my project’s node_modules folder to be used.&lt;/p&gt;

&lt;h1&gt;
  
  
  Component Code
&lt;/h1&gt;

&lt;p&gt;This is the simplest part because there really is no logic to speak of. I am simply creating a visual component with no other behavior.&lt;/p&gt;

&lt;p&gt;Inside the file &lt;code&gt;loading.component.ts&lt;/code&gt;, the generated code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-loading',
  templateUrl: './loading.component.html',
  styleUrls: ['./loading.component.scss'],
})
export class LoadingComponent implements OnInit {

  constructor() { }

  ngOnInit() {}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I said, there is not much here. After the two imports is the &lt;code&gt;@Component&lt;/code&gt; decorator, which defines how the component will be implemented. &lt;code&gt;selector&lt;/code&gt; defines the custom component’s HTML tag. This is how the component will be placed on a page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;app-loading&amp;gt;&amp;lt;/app-loading&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next two lines tell the Angular compiler (and us) where to find the markup and styles for the component, respectively.&lt;/p&gt;

&lt;p&gt;Next is the class body itself, consisting of two empty functions. I need neither of those, so will delete them entirely, replacing them with two variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-loading',
  templateUrl: './loading.component.html',
  styleUrls: ['./loading.component.css'],
})
export class LoadingComponent {
  @Input() label = '';
  @Input() shown = false;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@Input&lt;/code&gt; decorators tell Angular to expose those two variables as attributes on the custom HTML tag.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;label&lt;/code&gt; will be bound to some text in the HTML so that I can tell the user exactly &lt;em&gt;what&lt;/em&gt; is loading. If you do not need that, you could eliminate it entirely.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;shown&lt;/code&gt; allows the host to show or hide the component as necessary. Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;app-loading label="Loading data now..." [shown]="isLoading"&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this example markup, I have hard-coded the loading message, but have bound the &lt;code&gt;shown&lt;/code&gt; attribute to a variable on the host component. Whenever &lt;code&gt;isLoading&lt;/code&gt; is true, the loading component will be visible; otherwise it will be hidden. That is all the host needs to be concerned with. How the visibility is implemented inside the loading component is irrelevant to the host.&lt;/p&gt;

&lt;h1&gt;
  
  
  Markup
&lt;/h1&gt;

&lt;p&gt;Now let us take a look at the markup. This, too, is pretty simple, almost trivial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="wrapper" [ngClass]="{hidden: !shown}"&amp;gt;
  &amp;lt;img src="/assets/img/loading.gif"&amp;gt;
  &amp;lt;h1&amp;gt;Please Wait While We Complete Your Request&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component consists of a single &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with a class called &lt;code&gt;wrapper&lt;/code&gt;. We will see more of that in the next section on styling. Inside this &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; are three more elements:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5SK9eEeD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://walkingriver.com/assets/img/loading.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5SK9eEeD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://walkingriver.com/assets/img/loading.gif" alt="Loading image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag pointing at an animated gif. This is one I am not really happy with. I would prefer to isolate the image so that using this component is a simple matter of dropping it into another project. However, this is not about component reuse. If it were, I would probably encode the image as a BASE-64 string and include it directly in the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag itself.&lt;/li&gt;
&lt;li&gt;A title represented by an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag containing a hard-coded message to the user.&lt;/li&gt;
&lt;li&gt;The final piece of content is a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag with its text bound to the &lt;code&gt;@Input() label&lt;/code&gt; field on the component. Whatever the host component passes as the &lt;code&gt;label&lt;/code&gt; attribute will be displayed here.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Styling
&lt;/h1&gt;

&lt;p&gt;The real magic happens in the component’s stylesheet. I will show the entire thing, followed by an explanation of the relevant sections. The stylesheet is SCSS, but it really does not need to be. The code uses no specific SCSS features, and should probably be renamed with the .css extension. I will leave that as an exercise for the reader.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h1 {
  line-height: 30px;
  font-size: 24px;
}

img {
  width: 74px;
  height: 74px;
  display: inline-block;
}

.hidden {
  display: none;
}

.wrapper {
  text-align: center;
  position: absolute;
  z-index: 9000;
  width: 480px;
  height: 326px;
  top: 100px;
  left: 50%;
  margin-left: -215px;
  background-color: #ffffff;
  outline: 9999px solid rgba(217, 217, 217, 0.95); 
  font-weight: 400;
  line-height: 18px;
  padding: 60px 20px 20px 20px;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  h1
&lt;/h2&gt;

&lt;p&gt;The first rule is for the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag, and it is pretty straightforward. It simply sets the font-size to 30px, and the line-height to a slightly lower value. These values do not materially change the component. They are purely aesthetic and you could change them to reflect your own personal style. One thing of note is that the Loading Component will inherit its host’s font selection, whatever that may be.&lt;/p&gt;

&lt;h2&gt;
  
  
  img
&lt;/h2&gt;

&lt;p&gt;The image tag, as I mentioned above, is hard-coded to a specific animated gif. The style sheet sets its size to be a 74px square, and sets it to display as an inline-block. Without that rule, CSS would not honor the width and height.&lt;/p&gt;

&lt;h2&gt;
  
  
  .hidden
&lt;/h2&gt;

&lt;p&gt;The component’s visibility is driven by this class. The wrapping &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; either does or does not have this class set, based on the value of the &lt;code&gt;shown&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;Why did I not put the &lt;code&gt;hidden&lt;/code&gt; class on the host and let the host handle it directly? The reason I wanted to use &lt;code&gt;shown&lt;/code&gt; is so that I could change the visibility implementation at will, without changing any of the host code.&lt;/p&gt;

&lt;p&gt;For example, I could add some CSS animation or implement some other complex code, all without the host components even knowing about it. They would continue to set &lt;code&gt;[shown]&lt;/code&gt; as they do now.&lt;/p&gt;

&lt;h2&gt;
  
  
  .wrapper
&lt;/h2&gt;

&lt;p&gt;This is the big one, so I will show the code again for convenience, explaining it as I go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.wrapper {
  text-align: center;
  position: absolute;
  z-index: 9000;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These first lines are just a bit of setup. They indicate that everything inside the wrapper will be centered, text and images both. The component will be positioned at an absolute position on the screen. The &lt;code&gt;z-index&lt;/code&gt; of 9000 is a relative position of depth. Elements with larger numbers appear “on top of” or “in front of” elements with a z-index value that is smaller. Setting the loading component’s z-index to 9000 gives it a decent likelihood that no other elements will appear in front of it. Should you find that is not the case, set a higher value. Browsers do not seem to have a standard “maximum” value, but most modern browsers should allow values up to 2&lt;sup&gt;31&lt;/sup&gt; - 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  width: 480px;
  height: 326px;
  top: 100px;
  left: 50%;
  margin-left: -215px;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This next block helps to position the loading component. It sets a fixed width and height, and positions the top of the component at 100px from the top of the screen. Then it does something a bit clever. The component’s left side is set at 50% of the host’s width. Then it sets a negative margin of half the component’s width. This effectively causes the entire component to be perfectly centered horizontally inside the host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  background-color: #ffffff;
  outline: 9999px solid rgba(217, 217, 217, 0.95); 
  font-weight: 400;
  line-height: 18px;
  padding: 60px 20px 20px 20px;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can see some various rules that dictate how the component looks. It has a background color of white, indicated by the value &lt;code&gt;#ffffff&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The clever bit I find is the next line: outline. The component’s outline is being defined as a 95% opaque (i.e., 5% transparent) solid gray line 9999px wide. This ends up covering the entire host component with the outline, preventing it from being selectable.&lt;/p&gt;

&lt;p&gt;The last three lines set the text font-weight to 400 (normal), a default line-height of 18px, and some internal padding to provide whitespace.&lt;/p&gt;

&lt;p&gt;And that is the entire component!&lt;/p&gt;

&lt;h1&gt;
  
  
  Use
&lt;/h1&gt;

&lt;p&gt;I hinted at its use above, but there are three things you would need to use it in your own project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Include the source files.&lt;/li&gt;
&lt;li&gt;Ensure that the component is declared and exported in whatever Angular module you intend to use it.&lt;/li&gt;
&lt;li&gt;Supply the HTML markup to call it, which looks like this.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;app-loading [label]="loadingText" [shown]="isLoading"&amp;gt;&amp;lt;/app-loading&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this sample code, I am using Angular’s attribute binding syntax to bind the &lt;code&gt;label&lt;/code&gt; and &lt;code&gt;shown&lt;/code&gt; attributes to the host component’s &lt;code&gt;loadingText&lt;/code&gt; and &lt;code&gt;isLoading&lt;/code&gt; variables, respectfully. Changes to these variables on the host component will cause Angular to re-render the loading component as necessary.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Result
&lt;/h1&gt;

&lt;p&gt;When it is all assembled and working on an actual web application, this is what it might look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4wGY_HEA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://walkingriver.com/assets/img/loading-component.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4wGY_HEA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://walkingriver.com/assets/img/loading-component.gif" alt="Loading Component Animation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Angular components do not need to be complicated. In fact, sometimes they do not even need any imperative code. In this article I have created a simple Loading Component that can be easily reused anywhere in my application.&lt;/p&gt;

&lt;p&gt;Further, with just a little more effort, I could build a completely standalone component that I could drop into any project I wish.&lt;/p&gt;

&lt;p&gt;What do you think? How could this component be improved? Let me know your thoughts.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Test-Driven Development in Angular</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Mon, 26 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/test-driven-development-in-angular-30g7</link>
      <guid>https://dev.to/walkingriver/test-driven-development-in-angular-30g7</guid>
      <description>&lt;p&gt;I tried something "new" this afternoon. I built an Angular service in a true TDD fashion. I wrote the tests first, discovering the service interface along the way. This is how it went. I invite you to follow along.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;I am not a fan of writing unit tests for Angular apps. The tooling I am using (Jasmine and Karma) feel like afterthoughts. They work and they have gotten much better over the past few years, but they still seem like they were written to bolt onto Angular, rather than being built as part of the ecosystem.&lt;/p&gt;

&lt;p&gt;Then I started thinking that maybe the problem is with me. Maybe I despise writing tests because I have not truly adopted test-driven-development in my Angular apps. I used to use TDD all the time with .NET and C#. &lt;/p&gt;

&lt;p&gt;So today I decided to go back to that philosophy and build a modest service using strict TDD principles. This is how it went.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Service
&lt;/h1&gt;

&lt;p&gt;The service itself is simple enough. I want to build a means of setting and retrieving two different unique IDs my app can use when making service calls. The first is a "conversation ID" that will be set as an HTTP header for all network calls for a specific user for a given session. It will not change until the application user manually refreshes the screen, closes the browser, or logs out and back in.&lt;/p&gt;

&lt;p&gt;The second is the "correlation ID." This will also get sent with each HTTP call, but it changes with every request. &lt;/p&gt;

&lt;p&gt;Not only will these IDs be set as custom HTTP headers on all web requests, they will be logged with all such requests and responses. They can then be used to correlate several layers of service requests and responses back to the user and high-level function that initiated them.&lt;/p&gt;

&lt;p&gt;The name of my service is simply &lt;code&gt;correlation&lt;/code&gt;. I created it with this Angular CLI command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ng g service services/correlation/Correlation

CREATE src/app/services/correlation/correlation.service.spec.ts &lt;span class="o"&gt;(&lt;/span&gt;382 bytes&lt;span class="o"&gt;)&lt;/span&gt;
CREATE src/app/services/correlation/correlation.service.ts &lt;span class="o"&gt;(&lt;/span&gt;140 bytes&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates two files in their own folder at &lt;code&gt;./src/app/services/correlation&lt;/code&gt;. I got a nearly-empty service file and a test (spec) file with one test. &lt;/p&gt;

&lt;p&gt;As I usually do, pre-pending &lt;code&gt;npx&lt;/code&gt; causes the system to use the locally-installed Angular CLI. &lt;/p&gt;

&lt;h1&gt;
  
  
  The Generated Test
&lt;/h1&gt;

&lt;p&gt;I want to start by reviewing the test code that was generated by the Angular CLI. I do not mean for this to be a comprehensive introduction to testing, but I will explain the basics. It should be enough for you to follow along and also modify your own tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./correlation.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CorrelationService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CorrelationService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should be created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first &lt;code&gt;import&lt;/code&gt; line brings in the Angular testing class called &lt;code&gt;TestBed&lt;/code&gt;. This class contains most of the basic testing framework.&lt;/p&gt;

&lt;p&gt;The second pulls in the service to be tested, also known as the "System Under Test," or SUT. &lt;/p&gt;

&lt;h2&gt;
  
  
  describe
&lt;/h2&gt;

&lt;p&gt;With most JavaScript testing frameworks, tests are organized into one or more &lt;code&gt;describe&lt;/code&gt; functions. These can be nested, as you will see shortly. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;describe&lt;/code&gt; function is called at least two parameters.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The test label. In this case, the name of the service to be tested.&lt;/li&gt;
&lt;li&gt;The function that contains the tests themselves. Here it is an arrow function. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This function contains a single variable representing the service, but nothing is assigned to it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  beforeEach
&lt;/h2&gt;

&lt;p&gt;Directly inside this function is another function call, &lt;code&gt;beforeEach&lt;/code&gt;, which itself contains another arrow function. This function is called by the testing framework before every unit test. &lt;/p&gt;

&lt;p&gt;This one calls the &lt;code&gt;TestBed.configureTestingModule({})&lt;/code&gt;, and you can see that it is being passed an empty object as its only argument. This is the options, and can accept just about everything a normal Angular module can. Most tests use this to configure Angular's dependency injection system to inject test doubles required by the SUT. My service has no dependencies, so there is nothing to configure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Functions
&lt;/h3&gt;

&lt;p&gt;Not shown are some other functions that can contain setup/tear-down instructions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;beforeAll: called once before any tests are run.&lt;/li&gt;
&lt;li&gt;afterAll: called once after all tests have been run.&lt;/li&gt;
&lt;li&gt;afterEach: called after each unit test function.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  it
&lt;/h2&gt;

&lt;p&gt;This function defines a single unit test. You can create as many &lt;code&gt;it&lt;/code&gt; functions as you want inside your &lt;code&gt;describe&lt;/code&gt;. The generated test comes with a single &lt;code&gt;it&lt;/code&gt; function. Its signature matches that of &lt;code&gt;describe&lt;/code&gt;, in that it takes a label and a function defining the test.&lt;/p&gt;

&lt;p&gt;When combined with its enclosing &lt;code&gt;describe&lt;/code&gt;, the &lt;code&gt;it&lt;/code&gt; functions should read like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[describe Label] [it Label]: Pass/Fail&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thus, when you read the one generated test, it should look like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CorrelationService should be created: Pass&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Consider this phrasing when you create your own tests.&lt;/p&gt;

&lt;p&gt;There is a lot more to Angular testing than this, but I wanted to make sure I explained what you would be seeing below before I begun.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Tests and API
&lt;/h1&gt;

&lt;p&gt;There are three primary things I need the service to do for me. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give me the same conversation ID whenever I ask, unless one does not exist. In that case, it needs to give me a new one and return it.&lt;/li&gt;
&lt;li&gt;Give me a fresh correlation ID every time I request one. I should never get the same ID twice.&lt;/li&gt;
&lt;li&gt;Provide a way for me to force a fresh conversation ID.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These rules allowed me to come up with the following tests. Again, I am using Jasmine as my testing framework. I know a lot of people these days are using Jest, but the concepts should be the same regardless of what you use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./correlation.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CorrelationService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CorrelationService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should be created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resetConversationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return different values on subsequent calls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getConversationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return identical values on subsequent calls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getCorrelationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return different values on subsequent calls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if you are not intimately familiar with Angular testing in Jasmine, I think these tests are easily understood. &lt;/p&gt;

&lt;p&gt;Naturally, though, none of these tests will run. In fact, they will not even compile. The functions on the service do not yet exist.&lt;/p&gt;

&lt;h1&gt;
  
  
  Auto-generated Service Code
&lt;/h1&gt;

&lt;p&gt;Fortunately, VS Code will do the heavy lifting for me. All I have to do is put my edit cursor on one of the function names, click the yellow light-bulb (for Auto Fix), and choose &lt;code&gt;Add all missing members.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y3MJFm5W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/vs-code-add-missing.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y3MJFm5W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://walkingriver.com/assets/img/vs-code-add-missing.png" alt="VS Code adds missing service functionality"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code it builds is not ideal and will still require some editing, but at this point the tests will compile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Method not implemented.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Method not implemented.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Method not implemented.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Make Them Run (and Fail)
&lt;/h1&gt;

&lt;p&gt;Now I have code that compiles, implemented in such a way that all three tests will fail with an expected exception. The first thing I need to do is remove the exceptions. My class now looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am afraid one of those tests will now pass, but  should not. Each function call in the test code evaluates to &lt;code&gt;undefined&lt;/code&gt;. This causes the test &lt;code&gt;should return identical values on subsequent calls&lt;/code&gt; to pass, because &lt;code&gt;undefined&lt;/code&gt; equals &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I will have to edit the tests. I have two choices. I can add three more tests to ensure that no function returns &lt;code&gt;undefined&lt;/code&gt; or I can add a check for &lt;code&gt;undefined&lt;/code&gt; in the test that is checking for equality. &lt;/p&gt;

&lt;p&gt;Some purists believe that every test should have a single assertion/expectation. I tend to be more of a pragmatist. If you are testing one high level "thing," then it is fine to have multiple expectations in a single test.&lt;/p&gt;

&lt;p&gt;The new test now looks like this, and fails as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getConversationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return identical values on subsequent calls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// New code&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note I am only checking on the first result to be defined. If the first call is defined and the second is not, the second expectation will then fail. I will let you decide which approach makes sense for your project.&lt;/p&gt;

&lt;h1&gt;
  
  
  Make Them Pass
&lt;/h1&gt;

&lt;p&gt;According to TDD principles, the next step is to write the least amount of code that will cause the tests to pass. In theory, I should not have to touch the tests again. In practice, I probably will. This is a path of discovery, which I am writing as I go. Thus, you are learning right along with me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mike&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mike&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mike&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, this will make the middle test pass, but not the others. It is time to think about how the service is &lt;em&gt;supposed to&lt;/em&gt; work. &lt;/p&gt;

&lt;h2&gt;
  
  
  UUID
&lt;/h2&gt;

&lt;p&gt;The business rules call for some sort of semi-unique identifier string. I plan to use a GUID or some variant thereof. &lt;/p&gt;

&lt;p&gt;After a few seconds (ok, a minute or so) of research, I found the &lt;a href="https://www.npmjs.com/package/uuid"&gt;UUID npm package&lt;/a&gt;{:target="_blank"}. I will it use to generate both my conversation and correlation IDs.&lt;/p&gt;

&lt;p&gt;Once the package is installed in my project, the CorrelationService now looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the tests pass or fail as expected. &lt;/p&gt;

&lt;h1&gt;
  
  
  Make It Right
&lt;/h1&gt;

&lt;p&gt;This code looks pretty good, almost complete. There are two things I think are missing. &lt;/p&gt;

&lt;p&gt;The first is obvious: Subsequent calls to &lt;code&gt;getConversationId&lt;/code&gt; need to return the same value. This means I need a place to store the value. There is also the scenario of the ID's initial value. How do we handle that?&lt;/p&gt;

&lt;p&gt;I will tackle the second scenario first by modifying &lt;code&gt;getConversationId&lt;/code&gt; to return the stored value, and also by modifying &lt;code&gt;resetConversationId&lt;/code&gt; to set the stored value. This will cause the tests to fail, but that is why we write them in the first place. Right?&lt;/p&gt;

&lt;p&gt;My modified service looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the tests pass, because I had the foresight to call &lt;code&gt;resetConversationId&lt;/code&gt; in the test expecting equality. In reality, this was not a good idea. My motive was good, but I do not believe a user should be forced to call &lt;code&gt;resetConversationId&lt;/code&gt; before calling &lt;code&gt;getConversationId&lt;/code&gt;. That should be up to the code.&lt;/p&gt;

&lt;p&gt;So, now I want to remove the call to &lt;code&gt;resetConversationId&lt;/code&gt; from the test, which will cause that test to fail. &lt;/p&gt;

&lt;p&gt;To enable that code to pass again, I need to modify the service to ensure there is a value before returning it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all my tests pass, the service does the modest job it is meant to do, and my test coverage looks good. &lt;/p&gt;

&lt;h1&gt;
  
  
  The Final Test
&lt;/h1&gt;

&lt;p&gt;Here is the final set of tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./correlation.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;fdescribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CorrelationService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CorrelationService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should be created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resetConversationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return different values on subsequent calls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getConversationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return identical values on subsequent calls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getCorrelationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return different values on subsequent calls&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secondId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  The Final Service
&lt;/h1&gt;

&lt;p&gt;Here is the entire service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CorrelationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;getCorrelationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I probably could also dispense with the empty constructor, but something in the back of my mind is preventing me from deleting it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Refactoring the Service
&lt;/h1&gt;

&lt;p&gt;After I finished writing this, it occurred to me that there is a better way to initialize the service than with the &lt;code&gt;||&lt;/code&gt; in &lt;code&gt;getConversationId&lt;/code&gt;. Why not use the constructor to do its job and construct the object and initialize its internal state?&lt;/p&gt;

&lt;h2&gt;
  
  
  Before
&lt;/h2&gt;

&lt;p&gt;As you may recall (or just look up and see), the &lt;code&gt;getConversationId&lt;/code&gt; function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the value of &lt;code&gt;this.conversationId&lt;/code&gt; is not defined, the conditional "or" will cause the function on the right side to be executed. That function's side-effect is to initialize the value. TypeScript conditional "short-circuiting" prevents it from being called if &lt;code&gt;this.conversationId&lt;/code&gt; already contains a value. &lt;/p&gt;

&lt;p&gt;In this case, it is simple enough to follow, but you may be able imagine that in more complex classes it may not be.&lt;/p&gt;

&lt;h2&gt;
  
  
  After
&lt;/h2&gt;

&lt;p&gt;Instead, I will move the call to &lt;code&gt;resetConversationId&lt;/code&gt; into the constructor, guaranteeing that &lt;code&gt;this.conversationId&lt;/code&gt; will always have a value. Thus, I can delete the conditional check from the latter function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetConversationId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;getConversationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To me, this is much simpler code and captures the meaning more clearly than before. Anyone looking at this code will understand that the service pre-initializes its state immediately.&lt;/p&gt;

&lt;p&gt;The tests still pass, as they should. This ostensibly is why we write unit tests in the first place, to ensure that changes to the implementation do not break functionality.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;From start to finish, this experiment took me just over two hours to complete (2:30 - 4:45 PM). I spent another 15 minutes or so doing the above refactoring and writing about it.&lt;/p&gt;

&lt;p&gt;The tests were easy to write because the service itself did not exist when I began. By describing the tests as I expected them to work, the service API practically wrote itself. &lt;/p&gt;

&lt;p&gt;I am not convinced that a more complicated service or a UI component will be as easy to write in this manner, but over all I am pleased with the result.&lt;/p&gt;

&lt;p&gt;I will probably continue to develop the project this way, and can honestly recommend that everyone should give it a try some time. You may end being pleasantly surprised.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>To RxJS or Not to RxJS</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Mon, 12 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/to-rxjs-or-not-to-rxjs-4ao6</link>
      <guid>https://dev.to/walkingriver/to-rxjs-or-not-to-rxjs-4ao6</guid>
      <description>&lt;p&gt;Note: this is an excerpt from my upcoming book, &lt;a href="https://gum.co/angular-advocate"&gt;Angular Advocate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I am currently working on a project to uplift some AngularJS code to Angular 10. On a recent code review there were some concerns that came up about the heavy use of RxJS. I will attempt to address those concerns in this post.&lt;/p&gt;

&lt;p&gt;Of particular note were the following assertions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;RxJS is hard to learn/read.&lt;/li&gt;
&lt;li&gt;RxJS is hard to test.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Hard to Learn
&lt;/h1&gt;

&lt;p&gt;Admittedly RxJS has a steep learning curve. That said, it should only take an week or so to become accustomed to using it. Once you understand the basics, the more esoteric operators are mostly a matter of finding them. There is a great website that makes this easier, which is where I found the &lt;code&gt;fromEvent&lt;/code&gt; and &lt;code&gt;combineLatest&lt;/code&gt; operators I use below. &lt;a href="https://rxjs.dev/operator-decision-tree"&gt;https://rxjs.dev/operator-decision-tree&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Hard to Test
&lt;/h1&gt;

&lt;p&gt;Testing Angular is itself often an exercise in frustration. The key is knowing what to test and what not to test. I wrote a &lt;a href="https://walkingriver.com/stop-testing-my-code/"&gt;blog post on that very subject&lt;/a&gt;. Some people go overboard and try to achieve 100% code coverage. I have found that more than about 80-85% is usually counter-productive. Often, people will write brittle (or worse, useless) tests to try to hit some magical coverage number.&lt;/p&gt;

&lt;p&gt;RxJS is no more difficult to test than Angular, assuming you write your code to be testable in the first place. This is not always the case, especially when you try to do too much with it at once. My strategy is to create observables that do one thing that I can explain in a comment. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convert input event into stream of search terms.&lt;/li&gt;
&lt;li&gt;Make web service call on selection change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These sorts of observables are reasonably straightforward to write and test. More importantly, they are also straightforward to read and to modify.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Code
&lt;/h1&gt;

&lt;p&gt;The code in question includes a custom dropdown with search capability. The idea is that you can bind a very long list of items and either select one or search for one by name. The component fires a custom DOM event, &lt;code&gt;selected-changed&lt;/code&gt;, to indicate that the user has made a new selection.&lt;/p&gt;

&lt;p&gt;The dropdown represents a list of “stores.” Whenever a store is selected, the app needs to make a web request to load products associated with that store.&lt;/p&gt;

&lt;p&gt;Though it renders and behaves like an HTML &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; tag, it does not actually contain a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element. Fortunately, this turned out to be irrelevant.&lt;/p&gt;

&lt;p&gt;The first challenge was to figure out what event was being thrown. The docs showed that I could use &lt;code&gt;(selected-change)="storeChange($event)&lt;/code&gt;, so I wired it up to see what the event looks like. It seemed to be exactly what I had hoped: a custom DOM event.&lt;/p&gt;

&lt;p&gt;The next step was to create an Observable I could subscribe to. I created a component variable I could use to subscribe to the event using Angular’s &lt;code&gt;@ViewChild&lt;/code&gt; decorator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ViewChild('storeList') storeListDropdown: ElementRef;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That led to this small bit of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.storeChanged$ = fromEvent(this.storeListDropdown.nativeElement, 'selected-changed')
  .pipe(tap(x =&amp;gt; {
    console.log(x);
  }));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I could verify whether or not I could truly subscribe to these custom events. A quick test showed that indeed I could. Hurdle one cleared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the service call
&lt;/h2&gt;

&lt;p&gt;Next, I needed to take the newly-selected value and use it to make a service call. For various reasons, I cannot show the actual call here, but I think I can show enough to get my point across.&lt;/p&gt;

&lt;h2&gt;
  
  
  I only care about selections
&lt;/h2&gt;

&lt;p&gt;It is possible for the user to select nothing, at which point I do not want to make a service call. So I added a filter to the observable’s pipe to ensure I am only getting selections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.storeChanged$ = fromEvent&amp;lt;CustomEvent&amp;gt;(this.storeListDropdown.nativeElement, 'selected-changed')
  .pipe(
    filter(x =&amp;gt; x.detail?.value?.length),
    tap(x =&amp;gt; {
      console.log(x.detail.value[0].value);
    }));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that I was able to confirm that it only fired when the user selects something. There is another use case to clear the results if the user selects nothing, but that is not currently important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the web call
&lt;/h2&gt;

&lt;p&gt;Next up was to make the web service call. I modified the &lt;code&gt;tap&lt;/code&gt; function to set a component variable that holds the selection, then added a &lt;code&gt;switchMap&lt;/code&gt; to call an Angular service that hides the actual HTTP call. Now it looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.storeChanged$ = fromEvent&amp;lt;CustomEvent&amp;gt;(this.storeListDropdown.nativeElement, 'selected-changed')
  .pipe(
    filter(x =&amp;gt; x.detail?.value?.length),
    tap(x =&amp;gt; {
      this.selectedStore = x.detail.value[0].value;
    }),
    switchMap(x =&amp;gt; this.getProducts(this.selectedStore))
  );

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could drop the &lt;code&gt;tap&lt;/code&gt; and done both the variable assignment and the service call inside of the &lt;code&gt;switchMap&lt;/code&gt; operator. I may eventually do that, but for now I like the way that each step is visually separated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search filter
&lt;/h2&gt;

&lt;p&gt;A single “store” in this project could return anywhere from 0 to potentially hundreds or thousands of products. Each record is small, so for now, we are keeping the filtering of products entirely client-side. To support that, the UI has a search box. Entering anything in the search box should cause the records to be filtered to those records matching the value entered. This called for another observable, which I will show in its entirety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.searchFilterChanged$ = fromEvent&amp;lt;InputEvent&amp;gt;(this.searchBox.nativeElement, 'input')
  .pipe(
    // tslint:disable-next-line: no-magic-numbers
    debounceTime(300),
    map(_ =&amp;gt; {
      this.searchFilter = this.searchBox.nativeElement.value;

      return this.searchFilter;
    }),
    distinctUntilChanged(),
    startWith('')
  );

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I start by creating an observable from the HTML &lt;code&gt;input&lt;/code&gt; event. That fires an event for every change to the text box. This stream is then sent through another RxJS pipe to do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The observable is “debounced” so that any value is delayed by 300ms. This prevents the events from coming in too fast as the user types.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;distinctUntilChanged&lt;/code&gt; operator ensures that only changes to the value are sent. In other words, if the user repeated presses the space bar and the backspace key in rapid succession, resulting in no change to the text box, my code will never see it. It is unlikely, but possible.&lt;/li&gt;
&lt;li&gt;Inside of &lt;code&gt;map&lt;/code&gt;, I assign the value of the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element to a component variable named &lt;code&gt;searchFilter&lt;/code&gt; and return that value. This successfully converts the event into a string I can use as a search value later.&lt;/li&gt;
&lt;li&gt;The final operator is &lt;code&gt;startWith('')&lt;/code&gt;, which will be important later. This initializes the observable stream with an empty string value.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Pager component
&lt;/h2&gt;

&lt;p&gt;My final component is another custom one that handles paging. As I said, the results of the web service can potentially contain thousands of records, so I want to provide a user a simple way of paging through those results. Like my other custom component, it also fires a &lt;code&gt;CustomEvent&lt;/code&gt; called &lt;code&gt;nav-selected&lt;/code&gt;. I again used &lt;code&gt;fromEvent&lt;/code&gt; and set it up similarly to the first one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.pagerChanged$ = fromEvent&amp;lt;CustomEvent&amp;gt;(this.pager.nativeElement, 'nav-selected')
  .pipe(
    map(x =&amp;gt; x.detail.currentPage),
    startWith(1)
  );

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the simplest of the bunch. Inside its pipe, I map the details from the custom event into the page the user selected. Once again, I start the stream with a default value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combine them all
&lt;/h2&gt;

&lt;p&gt;Why did I go through all of this instead of simply using angular event binding? This is why. Now that each event is its own individual stream of events, they can easily be combined using the RxJS operator &lt;code&gt;combineLatest&lt;/code&gt;. This operator accepts an array of observables as its input and returns a single observable. As its name implies, this observable fires when any of its input observable values changes. Whenever that happens, the subscriber gets the latest value of each of its constituent observables.&lt;/p&gt;

&lt;p&gt;The caveat for this operator is that it will not emit an event until each observable in its input array has fired at least once. This is why each of my observables above (except the first one) used &lt;code&gt;startWith&lt;/code&gt; to set an initial value. The store dropdown did not need one because until the user selects a store, there is no reason to do anything.&lt;/p&gt;

&lt;p&gt;How does this all work in practice? Every time the user selects a new store, changes the search filter, or selects a new page on the pager, my subscriber gets the most recent value of each of those three observables. Here is the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.filteredProducts$ = combineLatest([
  this.storeChanged$,
  this.searchFilterChanged$,
  this.pagerChanged$
]).pipe(
    map(([products, search, page]) =&amp;gt; this.filterProducts(products, search, page))
  );

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside of the &lt;code&gt;map&lt;/code&gt; operator, you can see that each value from the array of observables is passed into its arrow function. I pass those values into a pure function on my component called &lt;code&gt;filterProducts&lt;/code&gt;. This function handles the filter and paging on the &lt;code&gt;products&lt;/code&gt; array to return a new array of products.&lt;/p&gt;

&lt;p&gt;Keep in mind that the return of &lt;code&gt;combineLatest&lt;/code&gt;, which is assigned to &lt;code&gt;this.filteredProducts$&lt;/code&gt;, is itself an observable. This is important because of what happens next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inside the template
&lt;/h2&gt;

&lt;p&gt;Because of the composition of these three independent observables into a single observable, binding to its results is almost trivial. Inside my HTML template, I have the following markup inside an HTML &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;tr *ngFor="let product of filteredProducts$ | async"&amp;gt;
  ...
&amp;lt;/tr&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using Angular’s &lt;code&gt;async&lt;/code&gt; pipe, I never need to subscribe or unsubscribe to the observable. Angular handles all that for me. Whenever any of the three source observables changes, the table will update to reflect those changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benefit of this approach
&lt;/h2&gt;

&lt;p&gt;I started down this path of using RxJS for user events because I wanted to isolate each event and hide the irrelevant details from its consumers. By using &lt;code&gt;fromEvent&lt;/code&gt; I am able to tweak and modify each event to get exactly the behavior I am looking for, separate from the others.&lt;/p&gt;

&lt;p&gt;With that, the final observable created with &lt;code&gt;combineLatest&lt;/code&gt; requires no additional manipulation. I can pass the values to a pure function to get exactly the data to be displayed, and then bind that data with a simple &lt;code&gt;*ngFor&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt; pipe.&lt;/p&gt;

&lt;p&gt;Testing each observable stream independently from the others also becomes almost trivial.&lt;/p&gt;

&lt;p&gt;What I really like most about this approach, though, is how extensible it is. When I first wrote this code, I only had the custom dropdown and the text input box for searching. It was all working properly when I decided to add the custom pager control. The result was that I was able to include the pager, set up and test its custom event, and then simply wire it up to the &lt;code&gt;combineLatest&lt;/code&gt; operator by adding it to the input array. Then I added it as an input to my &lt;code&gt;filterProducts&lt;/code&gt; function.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Do Others Think?
&lt;/h1&gt;

&lt;p&gt;I double-checked my philosophy with some people I consider experts, both inside and outside of team. Here are some of the responses I received (mostly paraphrased):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That code is awesome!&lt;/li&gt;
&lt;li&gt;For me it’s a tradeoff between simplicity (Angular’s event bindings) and power (RxJS fromEvent). In the majority of cases I only need the simple event bindings. As soon as you try doing something special (debounce, filter, delay, etc.), use RxJS.&lt;/li&gt;
&lt;li&gt;Observables are a heavy pill to swallow. If you get why you’d use it (sync/async feel the same, pure function pipelines of data, deterministic, easier to unit test), cool. Otherwise, hide in the Model layer so dev can play away from ‘em elsewhere.&lt;/li&gt;
&lt;li&gt;I struggled for a long time to understand RxJs (still don’t get everything) but once I had a use case, it was a no-brainer to use the library.&lt;/li&gt;
&lt;li&gt;Every time I have to touch rxjs code it’s a major PITA and source of bugs for me. I’m not intelligent enough to understand it, no matter how many rxjs tutorials I read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interestingly, some suggested that learning to use state management with NgRx might result in a lower learning curve than raw RxJS. My experience has been the opposite.&lt;/p&gt;

&lt;p&gt;Ultimately, I recommend that you use the best tool for the job. If that means going a little outside of your comfort zone to learn RxJS, so be it.&lt;/p&gt;

&lt;p&gt;Everything you see above took about three days start-to-finish to get right, including the unit tests I have not shown. When I began, I knew almost nothing about any of these RxJS operators I ended up using.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Got My First Programming Job</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Fri, 28 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/how-i-got-my-first-programming-job-32k7</link>
      <guid>https://dev.to/walkingriver/how-i-got-my-first-programming-job-32k7</guid>
      <description>&lt;p&gt;I wanted to become a software developer from the time I wrote my first “Hello World” app in 9th grade. After graduating high school in 1985, I entered the University of Maryland’s Computer Science program. It did not go well. I completed only four semesters of college over the next five years. My dream of becoming a professional developer faded. In 1995, nearly ten years after high school, I finally landed my first paid programming job. Here is how I did it.&lt;/p&gt;

&lt;p&gt;A lot happened to me in the decade between high school and my first programming job. The highlights, in order were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Started and dropped out of college twice.&lt;/li&gt;
&lt;li&gt;Helped manage two local pizza delivery chains, neither of which was a Dominos.&lt;/li&gt;
&lt;li&gt;Sold high-end TVs and audio equipment at a local AV chain store.&lt;/li&gt;
&lt;li&gt;Created and folded three companies: desktop publishing, wedding photography, and business equipment refurbishment.&lt;/li&gt;
&lt;li&gt;Packed up and moved from Maryland to New Hampshire to “start over,” leaving behind my former life as a devout atheist.&lt;/li&gt;
&lt;li&gt;Got a job at a New England-based Apple Macintosh dealer.&lt;/li&gt;
&lt;li&gt;Met and married a wonderful woman and began a life together.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  I Used Macs Before They Were Cool
&lt;/h1&gt;

&lt;p&gt;Before I moved to New Hampshire, I had already arranged for a job. I started as a retail sales rep at Computer Town in Nashua on July 27, 1992. We sold the complete line of Apple Macintosh products. System 7 had been released just over a year prior. Though Macs were starting to become popular, Windows 3.1 still held the lion’s share of the PC market.&lt;/p&gt;

&lt;p&gt;I quickly learned all I could about the computer business and our product lines. I leveraged my experience refurbishing business equipment into a thriving used Mac business in an unused room on the second floor of our store.&lt;/p&gt;

&lt;p&gt;Due to being consistently one of the top sales reps in our location, I was selected as one of the few to represent the company at MacWorld Boston every year, and MacWorld San Francisco the one time we attended.&lt;/p&gt;

&lt;h1&gt;
  
  
  Once a Programmer…
&lt;/h1&gt;

&lt;p&gt;I enjoyed my time selling computers, which helped me hone my skills working directly with people. In the back of my mind, however, there was still an emptiness. I never shed the dream of programming computers for a living. During my spare time, I learned all I could about how to program the Macintosh computers I sold.&lt;/p&gt;

&lt;h2&gt;
  
  
  A/UX
&lt;/h2&gt;

&lt;p&gt;Did you know that Apple had a Unix-based operating system before OS X? It was called &lt;a href="https://en.wikipedia.org/wiki/A/UX"&gt;A/UX&lt;/a&gt; and was based on UNIX System V Release 2.2. It looked like System 6 but had a real Unix core under the covers. Naturally, the geek in me needed to run this OS. I bought a copy and installed it on the Mac Quadra on my desk at work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filemaker Pro
&lt;/h2&gt;

&lt;p&gt;One of my more ambitious projects was when I created my own customer management application in Filemaker Pro, a powerful (at the time) database system. I used it to help me communicate with my repeat customers, track sales, and generally provide me with an excuse to do some level of programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Warrior
&lt;/h2&gt;

&lt;p&gt;Who remembers &lt;a href="https://en.wikipedia.org/wiki/CodeWarrior"&gt;Code Warrior&lt;/a&gt;? It was an integrated development environment (IDE) for building apps on the Macintosh. I bought a copy as soon as I could and started writing Pascal and C programs.&lt;/p&gt;

&lt;h1&gt;
  
  
  Usenet News (nh.jobs)
&lt;/h1&gt;

&lt;p&gt;One day I was perusing job listings on nh.jobs on usenet using a text-based news-reader. Netscape Navigator was brand new and most internet tools were still text-only. I stumbled upon a job post for a Junior Programmer in Nashua. The body of the post mentioned C experience but indicated that most relevant technologies would be taught on the job. I was intrigued. Could this be my chance to become a paid software developer?&lt;/p&gt;

&lt;h1&gt;
  
  
  Junior Programmer?
&lt;/h1&gt;

&lt;p&gt;Back in those days, most job posts of this nature were created by the company or even the person doing the hiring. Email spam was also not a huge issue, so most people simply included their email address right in the post. That was the case here. I quickly sent an email referencing the job post. I briefly described myself as a hobbyist programmer who had been self-taught since 1981. I ended with the question, “what do you consider a ‘junior programmer?’”&lt;/p&gt;

&lt;p&gt;The hiring manager emailed me back almost immediately, describing what he meant by junior. As I recall, the company was looking for someone they could teach application-specific integrated circuit design using &lt;a href="https://en.wikipedia.org/wiki/VHDL"&gt;VHDL&lt;/a&gt;. The only qualification he really wanted was &lt;em&gt;some&lt;/em&gt; programming ability. He asked me if I could implement a doubly-linked list with operations to add and remove elements. I accepted his challenge, wrote a short command line program in C, and sent the source code to him a few hours later.&lt;/p&gt;

&lt;h1&gt;
  
  
  Interview
&lt;/h1&gt;

&lt;p&gt;He invited me to his office for an official job interview. At this point, I was still feeling a bit nervous. I was not sure I was ready to leave the known for the unknown. I discovered the biggest hurdle I would face during the interview.&lt;/p&gt;

&lt;p&gt;As I mentioned, the company made integrated circuits using VHDL, a language I had never heard of. The manager gave me a brief overview and it looked pretty straightforward. Two things gave me pause:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The entire company was on Windows. I had never used Windows before in my life. I had been a die-hard “Mac guy” since college.&lt;/li&gt;
&lt;li&gt;The salary was fully 20% lower than I had been making at Computer Town.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;They offered me the job, but I told them I needed to discuss it with my wife. She and I decided that taking a pay cut at this point probably made sense, as it would lead to better opportunities in the future. We suspected there was not much room for growth in retail.&lt;/p&gt;

&lt;p&gt;Our decision made, I emailed the manager that I would accept their offer. The next day I gave my two-weeks’ notice to my manager at Computer Town, and started the new job less than a month later.&lt;/p&gt;

&lt;h1&gt;
  
  
  It Did Not Last
&lt;/h1&gt;

&lt;p&gt;As it turns out, the job only lasted a few months. I quickly discovered a blind spot for hardware programming. Although the syntax of VHDL was straightforward enough, programming ASICs was very confusing to me and unforgiving. Something about the process eluded me. Perhaps if I were to try it again, with 25+ years of experience, it might not seem so bad.&lt;/p&gt;

&lt;p&gt;One day my manager and the owner invited me into a conference room for a brief meeting. The owner asked me how I thought things were going. He knew things were not going well, and I confirmed it. He made me an offer that seemed odd at the time. If I chose to resign immediately, the company would provide me one month’s pay, and cover the cost of my health insurance for the rest of the year. As it turns out, resigning prevented me from filing for unemployment benefits. It did not matter, as I found a new job just a few weeks later.&lt;/p&gt;

&lt;p&gt;As we were wrapping up, the manager had a final piece of advice for me. He suggested that I might want to consider a different career and give up my dreams of being a professional software developer.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lessons Learned
&lt;/h1&gt;

&lt;p&gt;Though my first real programming job was not the tremendous success I had hoped, it nonetheless served a valuable purpose. It changed the course of my career and my life. And I learned some things that I have tried to keep in mind ever since.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always keep learning. Had I not continued to learn and behave as a programmer, I would never have been able to land this job.&lt;/li&gt;
&lt;li&gt;Sometimes you need to take a step backward. Taking the pay cut was briefly painful, but having a real programming job on my resume opened doors for future opportunities.&lt;/li&gt;
&lt;li&gt;You may have to cut your losses. The job was not what I had hoped it would be, and I was not what they had hoped I would be. In the end, we were both better off cutting ties after only a few months.&lt;/li&gt;
&lt;li&gt;Always be on the lookout for opportunities and be willing to jump when they appear. Shortly before I lost this job, I had run into one of my Mac customers at Computer Town. He told me to call him if I ever wanted to be a Mac developer. After I lost this job, I called him and let him know what had happened. He offered me a job on the spot, at a salary a little higher than I had been making selling Macs.&lt;/li&gt;
&lt;li&gt;Take other’s advice with a “grain of salt.” Had I listened to my manager’s advice to consider a different career, I can only imagine where I would be today. I am sure that I would not be midway through my third decade as a software developer, and it can be assumed I would not have the &lt;a href="https://dev.to/why-disney-1"&gt;Most Magical Job on Earth&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>software</category>
      <category>programming</category>
    </item>
    <item>
      <title>My Disastrous 3-Year Programming Hiatus</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/my-disastrous-3-year-programming-hiatus-m10</link>
      <guid>https://dev.to/walkingriver/my-disastrous-3-year-programming-hiatus-m10</guid>
      <description>&lt;p&gt;Ten years into an otherwise successful software development career, I experienced significant burnout and walked away from a 6-figure salary. I quit my job, sold my house, moved to a remote part of New Hampshire, and did not work again in software development for almost three years.&lt;/p&gt;

&lt;p&gt;In mid-2005 I threw a temper tantrum and quit my high paid software development job at HP. Sure, I blamed it on factors other than myself, but looking back it was all on me. Not only did I quit my job, I walked away from my entire career in software development.&lt;/p&gt;

&lt;h1&gt;
  
  
  Real Estate
&lt;/h1&gt;

&lt;p&gt;I decided to switch gears entirely and chase real estate deals full-time. Why did I think I had a “snowball’s chance” in this endeavor. It was mostly due to some early successes some friends and I had over the prior year.&lt;/p&gt;

&lt;p&gt;Two friends and I partnered on some real estate deals during the mini boom of 2004. We bought and resold some foreclosed properties in Southern New Hampshire. Later that year, I successfully managed two very lucrative “flips” on my own and saved up a decent little nest egg. In early 2005 I took some of my profits and bought myself a Mustang Convertible. I was convinced that this was a better way to make a living than working for a huge corporation like HP. So when the opportunity presented itself, I gave my notice and walked away from a promising career.&lt;/p&gt;

&lt;h1&gt;
  
  
  Time to Move
&lt;/h1&gt;

&lt;p&gt;Once I left HP and decided I was leaving software development for good, I used some of my cash to buy a brand new home in Central New Hampshire, and then sold the home in Southern New Hampshire where I had been living.&lt;/p&gt;

&lt;p&gt;Once the move was complete and the other house sold, it was time to rev up the real estate machine I had been slowly building. Over the next few months I found a couple of new people to help with the tasks. One was a local real estate appraiser who was good with tools. The other was an unemployed friend from church. Both men were willing to work at no charge until after the sale of any house we bought. That alleviated any immediate cashflow issues. I still had to pay for materials, but labor was free until closing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reduced Success
&lt;/h1&gt;

&lt;p&gt;Unfortunately, unbeknownst to me at the time, the real estate market in New Hampshire was beginning to cool. Properties were just as easy to find and acquire but they were becoming harder to sell. Those that did sell quickly were at much lower profit margins. There were some I had to hold for months, often at burn rates of $1000 or more per month, not counting material costs. Deals profiting $20,000 or more were vanishing quickly.&lt;/p&gt;

&lt;p&gt;My next two deals netted me less than $10,000 total. Given that real estate flips can take a month or more to complete, this was not enough money to pay my expenses. We started dipping into savings.&lt;/p&gt;

&lt;h1&gt;
  
  
  Rent for Cash Flow?
&lt;/h1&gt;

&lt;p&gt;For my next purchase, the thought occurred to me that I could make more money keeping it for a few years and putting a tenant in it. Against the advice of everyone I knew, that is what I did. A few months went by just fine, and then the rent checks stopped. Attempts to reach the tenants were in vain.&lt;/p&gt;

&lt;p&gt;After two months of being unable to reach them, they finally sent me a letter saying they had moved out and that I could keep their security deposit. I immediately got my Realtor friend over to the house for a damage assessment. She called with the good news that the house was still in good shape, but that the tenants had left the 2-car garage/barn full of their stuff.&lt;/p&gt;

&lt;p&gt;We put the house on the market immediately, but I had to deal with the barn. New Hampshire law requires that landlords keep their tenants’ property safe and secure for a minimum of 30 days after they move out. You cannot simply throw it all away. It would take longer than a month to sell, so I was not too concerned.&lt;/p&gt;

&lt;p&gt;After the month elapsed, we still did not have an offer on the house. Neither did the tenant come back to claim the contents of the barn. We decided to lower the asking price of the house and have a garage/barn sale. It was the first time we really took inventory of what had been left. We found a really nice snowblower and grill, along with lots of clothes and housewares. Most everything sold in one weekend. What did not we donated.&lt;/p&gt;

&lt;p&gt;Eventually, the house did sell. After paying my crew, the real estate fees, etc., the profit was less than $4000 for about eight months of work. You would expect that I had just learned a valuable lesson, but the harder lessons were still to come.&lt;/p&gt;

&lt;h1&gt;
  
  
  Renovation Profits?
&lt;/h1&gt;

&lt;p&gt;Perhaps I could renovate the properties for a slightly better profit than a quick flip?&lt;/p&gt;

&lt;p&gt;A Realtor friend helped me find a house in a small town on the southwest corner of the state, bordering both Massachusetts and Vermont. The house was in serious need of work, but she was confident she could sell it once the current tenant moved out. I made the purchase sight-unseen. Over the next month, some friends and I made the 200-mile roundtrip drive to the house each weekday. We had to rebuild the kitchen and the downstairs bathroom; replace the roof over the dining room and repair water damage in the dining room ceiling; and repair fire damage in the garage. The entire house was in desperate need of new carpet, so it was all replaced.&lt;/p&gt;

&lt;p&gt;Once the work was complete, it did sell quickly. By this time, however, the real estate market was in a noticeable downturn. After factoring in all of the time, effort, and cash needed to sell the house, I netted $1500 on the sale.&lt;/p&gt;

&lt;h1&gt;
  
  
  Disasters Begin
&lt;/h1&gt;

&lt;p&gt;My track record since leaving HP was not turning into the success story I had envisioned. I had not yet lost money but was quickly burning through my savings. I was simply not earning enough money fast enough to pay my bills. And then I was hit with two complete disasters in rapid succession.&lt;/p&gt;

&lt;p&gt;I found a tiny but clean manufactured home on a huge, gorgeous lot with mountain views. I agreed to purchase it from the landlord and arrange a sale to the tenants. Once I took possession of the property, it became quickly apparent that tenants had no ability to buy it. After a few months of promises, I politely told them they would need to leave, so I could get it sold. I was happy letting them stay while they were trying to buy it, but I was not looking to be a long-term landlord. They agreed to go without a legal battle.&lt;/p&gt;

&lt;p&gt;A few weeks later I received a phone call from the Town. The house’s water use had doubled over the past month, which they thought was unusual. They sent an inspector to the house, who heard running water through the door. Not getting an answer when he knocked, he immediately shut off the water and called me.&lt;/p&gt;

&lt;p&gt;When I got to the house, I discovered that the tenants had left without telling me. They had also shut off the power, and thus the heat. This being the dead of winter in New Hampshire, the temperature tends to drop well below freezing. I entered the house to a disaster. One of the water supply lines had frozen and burst, and began filling the house with water. By the time I arrived, the water was gone. The water stains, on the other hand, were approximately 18 inches above the floor. The floor itself was gone in many places, having simply collapsed under the weight of so much water. The entire house was rendered uninhabitable.&lt;/p&gt;

&lt;p&gt;I ended up selling it at a major loss to a builder who wanted the lot.&lt;/p&gt;

&lt;h1&gt;
  
  
  They Stole My Kitchen
&lt;/h1&gt;

&lt;p&gt;Around the same time, I bought a tiny fixer-upper on the outskirts of the state capital. It was easily the worst-looking house on its street. It was more than 100 years old and it showed. The yard was pleasant and would make a nice starter home for a young family. We got to work repairing one bathroom and replacing the other. We refinished the hardwood floors, added new kitchen appliances, and rebuilt the front porch.&lt;/p&gt;

&lt;p&gt;Once the renovations were complete, I put it on the market at its estimated value of $225,000. By this time, the market had taken a definite turn for the worst. Nothing seemed to be moving. After a few false starts, I reluctantly agreed to rent the house for a year, hoping the market would turn and I could sell. Or perhaps hoping that the tenant would be able to buy it. I was fine either way.&lt;/p&gt;

&lt;p&gt;You can probably guess what happened. The rent check bounced. This tenant and I were on pretty good terms, so I emailed her to ask her what was going on. I heard nothing for about a week. Finally, she replied with an apology. She could not afford to stay and had already moved out.&lt;/p&gt;

&lt;p&gt;When I got to the house, I found that the locks had been changed. I summoned a locksmith and the local police department to help me enter my own property. What we discovered upon entering was nothing like I would have ever imagined. The kitchen was gone. Every appliance, cabinet, and counter top — gone, stolen. The rest of the house was destroyed. There were holes in the walls, obscene graffiti scrawled everywhere. The pristine hardwood floors were scuffed and scratched. The bathroom we had replaced was trashed. One of the bedroom walls had been used to hang a dartboard. You could see the outline where the board had been and the evidence of how badly they played.&lt;/p&gt;

&lt;p&gt;I immediately asked the police officer whether I could file a criminal complaint, for the kitchen theft if not outright vandalism. He replied that because my tenant had done it, it was not a criminal matter and that I would have to file a civil complaint.&lt;/p&gt;

&lt;p&gt;I was out of money and my insurance would not cover this type of damage. In desperation, I had my real estate agent put the house on the market as-is, as a “gut job” fixer. She managed to find a buyer willing to pay $80,000 for it. I accepted.&lt;/p&gt;

&lt;p&gt;The only thing of value I got from that house was a tennis racket I bought at a yard sale before I bought the house. It is probably the most expensive tennis racket ever sold, and I still play with it to this day.&lt;/p&gt;

&lt;h1&gt;
  
  
  Recovery Begins
&lt;/h1&gt;

&lt;p&gt;That house was my last real estate deal. Not only were my savings gone, but I was in the hole more than $200,000. I had no idea what I was going to do next. It was 2008 and I had been out of the software development world for three years.&lt;/p&gt;

&lt;p&gt;Then I got a call from a friend from high school. We discussed everything that had occurred. It was at that point he asked me something that has stuck with me ever since. “Have you considered that this isn’t your core competency?” He went on to remind me that ever since we met I exhibited a love and talent for computer programming unmatched by anyone else he knew.&lt;/p&gt;

&lt;p&gt;He offered me a part-time software development gig with his young firm, working remotely on a 9-1-1 telecom product. I gratefully accepted the contract, which turned into a full-time job a few months later. This led to future opportunities working on telephony projects for other companies, including one in which I was a minority shareholder. The experience I gained there enabled me to get a long-term contract with Dell in Round Rock, Texas, and ultimately my current job at Walt Disney World. These other roles and jobs may become future blog posts.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lessons Learned
&lt;/h1&gt;

&lt;p&gt;It took me three more years to dig out from under the disaster that was my brief real estate career. Those losses were losses of cash, home equity, and even some retirement savings. I have not speculated in real estate since.&lt;/p&gt;

&lt;p&gt;Some of the takeaways from this experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never risk more than you can stand to lose. I leveraged one property against another on more than one occasion. This led to a domino effect where a loss on one house led directly to a loss on the next, and so on.&lt;/li&gt;
&lt;li&gt;You make your money when you buy. Never assume the real estate market is always going up. Though it is true that long time horizons can sometimes bail you out of a mistake, there are still some areas that have not yet recovered.&lt;/li&gt;
&lt;li&gt;Rental property management is hard. Hire a professional property manager. Let them do their job.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, the most valuable lesson I learned came from my friend: Stick to your core competencies. It is one thing to make the occasional “small bet” where your downside is limited. That is not what I did.&lt;/p&gt;

&lt;p&gt;I let my pride and my emotions drive me away from what could have been a long and lucrative career with HP. I lost tens of thousands of dollars, destroyed my personal credit for years, and nearly threw away my career. Three years in the software industry is an eternity. I was fortunate to have been rescued by someone willing to take a chance on me after being away for so long. I will be ever grateful.&lt;/p&gt;

&lt;p&gt;Burn out is real. Every software developer I have ever met has felt it at one time or another. Let my experience be a warning to you not to throw away a promising career on a long-shot.&lt;/p&gt;

</description>
      <category>career</category>
      <category>software</category>
      <category>realestate</category>
    </item>
    <item>
      <title>Debugging AngularJS from VS Code with Docker Containers</title>
      <dc:creator>Michael D. Callaghan</dc:creator>
      <pubDate>Mon, 10 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/walkingriver/debugging-angularjs-from-vs-code-with-docker-containers-40f7</link>
      <guid>https://dev.to/walkingriver/debugging-angularjs-from-vs-code-with-docker-containers-40f7</guid>
      <description>&lt;p&gt;This post describes how I built a docker container that runs an express server, which serves an AngularJS app, written in TypeScript, with live reloading, remote Chrome debugging, with Source Maps served from a different directory, and VS Code breakpoints.&lt;/p&gt;

&lt;p&gt;The updates to this application now enable any developer to spin up a development Docker container quickly. Once running, it is reasonably straightforward to edit and debug the application with Chrome and Visual Studio Code, with the code executing inside of the container.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problem
&lt;/h1&gt;

&lt;p&gt;Given an AngularJS web app served by a Node/Express app, provide a Docker solution to simplify both deployment and development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Ensure existing Dockerfile will work to deploy into corporate container host. Modify it if necessary.&lt;/li&gt;
&lt;li&gt;Without affecting the production Dockerfile, provide a way for new developers to get their environment up and running quickly.&lt;/li&gt;
&lt;li&gt;(Optional) Provide a way for live editing of the running web app inside of the container.&lt;/li&gt;
&lt;li&gt;(Optional) Provide a way to connect Chrome Dev Tools to the built code.&lt;/li&gt;
&lt;li&gt;(Optional) Provide a way for Dev Tools to access source maps which are located outside of the built assets folder.&lt;/li&gt;
&lt;li&gt;(Optional) Connect VS Code Debugger from outside the container.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Constraints
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Must start with the official Dockerfile provided, which contains the base OS requirements for the particular Node environment and Docker host.&lt;/li&gt;
&lt;li&gt;The app is built with AngularJS 1.4 and may not be upgraded for this project.&lt;/li&gt;
&lt;li&gt;The primary development language for the application is TypeScript.&lt;/li&gt;
&lt;li&gt;The app is built with the Grunt CLI and a pre-tested grunt configuration, which may not be changed.&lt;/li&gt;
&lt;li&gt;The built application artifacts are bundled, minified, and packaged into the ./dist folder.&lt;/li&gt;
&lt;li&gt;Source maps are generated but stored alongside the original TS files under the ./src folder.&lt;/li&gt;
&lt;li&gt;The application uses many deprecated npm and bower packages, and even node itself is outdated.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Base Dockerfile
&lt;/h1&gt;

&lt;p&gt;The first thing I did was grab the official Dockerfile provided for hosting Node/Express applications for the appropriate version of Node. Unfortunately, this particular app requires an old, unsupported version of Node. That, however, is a problem for another day. The strategies I outline should work regardless of the Node version in use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:0.10.38-slim

# Expose PORT
EXPOSE 8626

# Set Current Working Directory
WORKDIR /app

# Set production environment
ENV NODE_ENV=production

## Copy dist folder to application
COPY dist/package.json /app/

RUN npm install

COPY dist /app

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The base image is prebuilt image internal to the company, but it is based on Debian “Buster.”&lt;/p&gt;

&lt;p&gt;The hope was that at the very least, the application could be built and installed with the official Dockerfile with no, or only minor modifications.&lt;/p&gt;

&lt;p&gt;Upon the initial &lt;code&gt;docker build&lt;/code&gt; command, the process hung at the &lt;code&gt;npm install&lt;/code&gt; phase. Something the application needs required a step that used python. This was something other teams here had encountered and had a known solution. I simply needed to add a line to the Dockerfile to install Python, which I did by adding this command immediately after setting the current working directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Install Python and supporting build tools
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
        python \
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*
# rm -rf ^ removes cache, reducing image size

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At that point, my &lt;code&gt;npm install&lt;/code&gt; got farther, but still failed due to an npm package that was being referenced with &lt;code&gt;git+ssh&lt;/code&gt; protocol. Our official Docker image intentionally omits git. Instead of installing git into the image, I spent a few hours investigating why that particular package was being included that way, and what it was ultimately doing.&lt;/p&gt;

&lt;p&gt;As it turned out, the package in question was only used to help manage bower package versions, and was only executed prior to the &lt;code&gt;bower install&lt;/code&gt;. As our official CICD system has its own way of managing that, it became clear that this particular npm package is only needed during local development. I moved the npm package to the &lt;code&gt;devDependencies&lt;/code&gt; section in package.json, and provided some new scripts that would only be run outside the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"postinstall": "npm run bower",
"prebower": "node ./scripts/npm/preinstall.js",
"bower": "bower install",
"postbower": "node ./scripts/npm/postinstall.js",

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent the &lt;code&gt;git+ssh&lt;/code&gt; package from being installed as part of the &lt;code&gt;docker build&lt;/code&gt;, I modified the final &lt;code&gt;npm install&lt;/code&gt; command in the Docker file to include the &lt;code&gt;--production&lt;/code&gt; flag, thus bypassing the &lt;code&gt;devDependencies&lt;/code&gt; entirely.&lt;/p&gt;

&lt;p&gt;Now, as soon as the &lt;code&gt;npm install&lt;/code&gt; command completes, npm will execute the three &lt;code&gt;bower&lt;/code&gt; scripts in order. Our CICD system runs &lt;code&gt;npm install&lt;/code&gt; outside of a container and should have no problem running these commands.&lt;/p&gt;

&lt;p&gt;The final Dockerfile now looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:0.10.38-slim

# Expose PORT
EXPOSE 8626

# Set Current Working Directory
WORKDIR /app

# Install Python and supporting build tools
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
        python \
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*
# rm -rf ^ removes cache, reducing image size

# Set production environment
ENV NODE_ENV=production

## Copy dist folder to application
COPY dist/package.json /app/

RUN npm install --production

COPY dist /app

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--production&lt;/code&gt; flag to &lt;code&gt;npm install&lt;/code&gt; skips installing anything listed under &lt;code&gt;devDependencies&lt;/code&gt;, so my &lt;code&gt;git+ssh&lt;/code&gt; issue never arises.&lt;/p&gt;

&lt;p&gt;The only potential downside I see is with the three new bower scripts. The &lt;code&gt;docker build&lt;/code&gt; calls &lt;code&gt;npm install&lt;/code&gt; as one of its final steps, but the older version of npm it is running does not execute the &lt;code&gt;postinstall&lt;/code&gt; command, so will not run the bower scripts. This works because of a required build step that has to occur outside of &lt;code&gt;docker build&lt;/code&gt;, namely a &lt;code&gt;grunt build&lt;/code&gt; that packages the entire application into the ./dist folder. This includes all of the bower dependencies, so a separate &lt;code&gt;bower install&lt;/code&gt; is simply not needed. In future versions where Node is upgraded, it might be required to run &lt;code&gt;npm install --production&lt;/code&gt; completely outside of the Dockerfile and simply copy the node_modules folder.&lt;/p&gt;

&lt;p&gt;As it is, everything should work &lt;em&gt;for now&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Local Development
&lt;/h1&gt;

&lt;p&gt;The next thing I wanted to do was to have a local docker image that could be used by new developers. Ideally, I would use the Dockerfile I already had working. However, it was not that simple. The first thing I realized is that none of the &lt;code&gt;grunt&lt;/code&gt; commands would work inside the container because only the built artifacts are copied into it. There is also no &lt;code&gt;grunt-cli&lt;/code&gt; installed into the container.&lt;/p&gt;

&lt;p&gt;As I mentioned above, we have a complete system of grunt commands. The commands are used to serve the app under development with live reloading, and also to build and package the final application for deployment.&lt;/p&gt;

&lt;p&gt;After a few failed experiments with the existing Dockerfile, I decided the most prudent course of action would be create a parallel &lt;code&gt;Dockerfile.dev&lt;/code&gt; that was mostly the same, but that I could modify to suit local development needs. This is the file I came up with, which I called &lt;code&gt;Dockerfile.dev&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:0.10.38-slim

# Expose PORT
EXPOSE 8627

# Set Current Working Directory
WORKDIR /app

# Set production environment
ENV NODE_ENV=development

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two files are identical, except I completely leave out the python installation, the production version’s &lt;code&gt;npm install&lt;/code&gt; and &lt;code&gt;COPY&lt;/code&gt; commands at the end. Instead, I handle the source code, npm, bower, and grunt a little differently.&lt;/p&gt;

&lt;p&gt;Specifically, I added a series of scripts to package.json to handle most of the complexity. This will enable a new developer to clone the repo, perform an &lt;code&gt;npm install&lt;/code&gt;, and then run a few more npm scripts to configure the Docker environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Docker Build
&lt;/h2&gt;

&lt;p&gt;First, I needed a simple way to build a docker image from the new Dockerfile.dev file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"docker:build": "docker build -f Dockerfile.dev -t my-cool-app-ui:latest .",

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, executing &lt;code&gt;npm run docker:build&lt;/code&gt; will create a new image based on the development Dockerfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Docker Container Execution
&lt;/h2&gt;

&lt;p&gt;Next, I wanted to provide a similar experience for managing the local docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"serve": "grunt serve --no-browser-sync",
"docker:start": "docker run -d --rm --privileged --name my-cool-app-ui --env-file .env -p 8627:8627 -v $PWD:/app my-cool-app-ui npm run serve",
"docker:stop": "docker kill my-cool-app-ui",

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because I did not want to install any npm packages globally into the container, I added &lt;code&gt;grunt-cli&lt;/code&gt; to the application’s &lt;code&gt;devDependencies&lt;/code&gt;. That enabled me to provide a &lt;code&gt;serve&lt;/code&gt; script in package.json that could launch grunt inside the container.&lt;/p&gt;

&lt;p&gt;The next bit of magic is inside the &lt;code&gt;docker:start&lt;/code&gt; script. It is a simple &lt;code&gt;docker run&lt;/code&gt; command. The parameters chosen are important.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detached/Daemon Mode
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;-d&lt;/code&gt; parameter causes the container to be started in a detached state, meaning it is not interactive. It will start and run in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove on Exit
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--rm&lt;/code&gt; parameter is for housekeeping purposes. When the container exits, it is removed immediately, freeing up any resources it was consuming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privileged Mode
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--privileged&lt;/code&gt; parameter provides elevated access to the container. This was recommended to me by a colleague, but I am not convinced it is necessary. I may remove it and see whether it still works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Container Name
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--name parameter&lt;/code&gt; simply provides a friendly name for the container, rather than one randomly-generated by Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--env-file .env&lt;/code&gt; parameter indicates that the container should be started with the environment variables specified in a local file &lt;code&gt;.env&lt;/code&gt;. In our applications, the &lt;code&gt;.env&lt;/code&gt; file contains environment-specific configuration and secrets that are needed at runtime, but are not stored with the application or in source control. These types of things could include database connection strings, authentication tokens, etc. Storing them in a local file that is not stored in source control keeps sensitive information out of publicly-visible files (like package.json or Dockerfile).&lt;/p&gt;

&lt;h3&gt;
  
  
  Port Mapping
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;grunt serve&lt;/code&gt; starts the Node/Express app, it is configured to listen on TCP port 8627. For some reason, the production version of the app is configured to listen on port 8626. The parameter &lt;code&gt;-p 8627:8627&lt;/code&gt; maps port 8627 locally to the container’s port 8627. I will revisit this inconsistency at some point and clarify which port it should be.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local Volume Mapping
&lt;/h3&gt;

&lt;p&gt;The next parameter &lt;code&gt;-v $PWD:/app&lt;/code&gt; maps the current directory to a directory inside the container called &lt;code&gt;/app&lt;/code&gt;. Recall that the Dockerfile indicates that the working directory for the container is &lt;code&gt;/app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For me, this was the real breakthrough. Rather than fighting with grunt, npm, and bower inside the container, I simply map the entire project folder into the container’s &lt;code&gt;/app&lt;/code&gt; folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Image to Use
&lt;/h3&gt;

&lt;p&gt;The parameter &lt;code&gt;my-cool-app-ui&lt;/code&gt; is the name of the Docker image to execute. This is second time this identifier is used in the command, which is an unfortunate naming on my part. I inadvertently ended up naming the image and the container identically. I intend to fix that in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command to Launch
&lt;/h3&gt;

&lt;p&gt;The final three parameters of the &lt;code&gt;docker run&lt;/code&gt; command, &lt;code&gt;npm run serve&lt;/code&gt;, represent the command that Docker should execute inside the container. In this case, I am executing the &lt;code&gt;serve&lt;/code&gt; script inside of package.json.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stopping the Container
&lt;/h2&gt;

&lt;p&gt;I also provided another script to stop the container, which simply executes the command &lt;code&gt;docker kill my-cool-app-ui&lt;/code&gt;. This stops the detached container named &lt;code&gt;my-cool-app-ui&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive Alternative
&lt;/h2&gt;

&lt;p&gt;Finally, I created an alternative version of the &lt;code&gt;docker run&lt;/code&gt; command that works interactively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"docker:run": "docker run -it --rm --privileged --name my-cool-app-ui --env-file .env -p 8626:8627 -v $PWD/:/app my-cool-app-ui /bin/bash",

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is essentially the exact command as the last one with two exceptions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It uses the &lt;code&gt;-it&lt;/code&gt; parameter to create an interactive session instead of a detached session.&lt;/li&gt;
&lt;li&gt;It executes &lt;code&gt;/bin/bash&lt;/code&gt; to create a shell instead of simply running &lt;code&gt;grunt serve&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to launch the container interactively, simply execute &lt;code&gt;npm run docker:run&lt;/code&gt;. To stop and remove the container, you just need to log out of it.&lt;/p&gt;

&lt;p&gt;This enabled me to experiment with various &lt;code&gt;grunt&lt;/code&gt; commands and view web logs while I was still trying to figure things out. I felt it was useful enough to keep.&lt;/p&gt;

&lt;h1&gt;
  
  
  Debugging the TypeScript
&lt;/h1&gt;

&lt;p&gt;The next (optional) requirement was the ability to use the Chrome debugger to set breakpoints in the application’s TypeScript source code. The problem was that the TypeScript and generated source maps are stored in completely different locations that the files generated by the &lt;code&gt;grunt serve&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The solution is far simpler than the amount of research and experimentation that went into it. It ultimately came down to including three lines of code into the project’s &lt;code&gt;tsconfig.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"sourceMap": true,
"mapRoot": "/src/client/my-cool-app-ui",
"sourceRoot": "/src/client/my-cool-app-ui"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These three lines need to be included in the &lt;code&gt;compilerOptions&lt;/code&gt; section of the file. The first line was already there, indicating that source maps should be created.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mapRoop&lt;/code&gt; and &lt;code&gt;sourceRoot&lt;/code&gt; lines took some time to get right. These values end up being prepended to the name of the sourcemap file, which is specified at the end of each generated &lt;code&gt;.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;When the browser downloads a JavaScript file, it checks the end of the file for the location of any source map. It then makes a separate HTTP request for the file found there. This works transparently when the source map file is located right next to the JavaScript file. In my case, however, that was not true. I discovered this by watching all of the HTTP 404 errors for each attempted request.&lt;/p&gt;

&lt;p&gt;My solution was to prepend a different path to each source map, which corresponded to the source folder. With the &lt;code&gt;mapRoot&lt;/code&gt; set appropriately, instead of an HTTP request that looks something like this…&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:8627/my-cool-app-ui/feature/coolthing.js.map&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We would get this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:8627/src/client/my-cool-app-ui/feature/coolthing.js.map&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That still resulted in a 404, even though the folder was now correct.&lt;/p&gt;

&lt;p&gt;The solution to this was simple, however. I modified the Express app, only in &lt;code&gt;development&lt;/code&gt; mode, to serve static files from the &lt;code&gt;src/&lt;/code&gt; folder. Voila! The source code and source map requests suddenly worked.&lt;/p&gt;

&lt;p&gt;At that point, the Chrome debugger was fully functional, including the ability to set break points directly inside the TypeScript files.&lt;/p&gt;

&lt;h1&gt;
  
  
  VS Code Debugging
&lt;/h1&gt;

&lt;p&gt;The final requirement was to make all of the debugging features work from inside of VS Code, which already supports Chrome debugging through its &lt;a href="https://github.com/Microsoft/vscode-chrome-debug"&gt;Debugger for Chrome Extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once this extension was installed, I only had to add the appropriate configuration inside of &lt;code&gt;.vscode/launch.json&lt;/code&gt;. This is the configuration that ultimately worked, which I believe is the default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "Launch Chrome",
  "request": "launch",
  "type": "pwa-chrome",
  "url": "http://localhost:3000/individual-entry-codes",
  "sourceMaps": true,
  "trace": false
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only thing I did was change the &lt;code&gt;url&lt;/code&gt; to a page I wanted to load, and the &lt;code&gt;trace&lt;/code&gt; property. I set its value to &lt;code&gt;true&lt;/code&gt; during my experiments, which provides more verbose logging in VS Code’s Debug Console. I set it back to &lt;code&gt;false&lt;/code&gt; for most ordinary uses.&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker Compose
&lt;/h1&gt;

&lt;p&gt;This document describes my adventures in getting the UI project up and running in Docker. However, there is also the API project to consider. In production, the two execute in separate Docker containers primarily so they can be scaled independently from one another. A load balancer sits in front of them to manage the traffic and to provide a single base URL, enabling them to share cookies. The load balancer acts as a proxy, routing calls to the API based on the URL route. The next step in my journey is to replicate that experience locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerizing the API
&lt;/h2&gt;

&lt;p&gt;As it turns out, executing the API project in Docker worked exactly the same was as I described above for the UI project, proving that the process is repeatable.&lt;/p&gt;

&lt;p&gt;Once I had both the UI and API projects running separately inside of their own Docker containers, I needed a way to route traffic between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  HA Proxy
&lt;/h2&gt;

&lt;p&gt;Enter &lt;a href="https://registry.hub.docker.com/_/haproxy/"&gt;HA Proxy&lt;/a&gt;. This docker image was recommended as the one to use, though I have also used &lt;a href="https://registry.hub.docker.com/_/nginx"&gt;Nginx&lt;/a&gt; and it works just fine. The are both serving the same function in this case. I will detail HA Proxy, as that is the one I used for this project.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is It?
&lt;/h3&gt;

&lt;p&gt;From the explanation in the Docker registry,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HAProxy is a free, open source high availability solution, providing load balancing and proxying for TCP and HTTP-based applications by spreading requests across multiple servers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is exactly what I needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;The hardest part about any of these tasks is figuring out the appropriate configuration settings. As I said, someone else gave me this, so it only took some minor tweaking.&lt;/p&gt;

&lt;p&gt;The Dockerfile itself was only two lines, and also comes straight from DockerHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM haproxy:1.7
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing exciting to see there. It simply grabs a specific (albeit older) version of HA Proxy and copies a config file.&lt;/p&gt;

&lt;p&gt;This is that config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global
    daemon # Run in the background

defaults # These apply to everything and were provided to me
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 50000
    timeout server 50000

frontend localnodes # Public-facing config
    bind *:3000 # Listen on port 3000
    mode http                             
    acl is_api_url path_beg -i /api/ # Check to see if path starts with /api/
    use_backend api_server if is_api_url # If so, forward to api_server
    default_backend static_server # Otherwise, default to static_server (UI)

backend static_server # Static (UI) Server Config
    mode http
    balance roundrobin
    option forwardfor # Forward traffic to...
    server web01 ui:8627 # A server called ui on port 8627

backend api_server # API Server Config
    mode http
    balance roundrobin
    option forwardfor # Forward traffic to...
    server web01 api:8181 # A server called api on port 8181

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only edits I made to this file were on the two server lines. They were set to &lt;code&gt;localhost&lt;/code&gt;, which worked when I ran the two Docker containers locally. They exposed their respective ports. However, when I decided to try using Docker Compose, I intentionally configured its network to hide those ports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker-Compose Configuration
&lt;/h3&gt;

&lt;p&gt;Now I will show the docker-compose.yaml file I came up with. The sole consideration for me was to have Docker automatically build (if necessary) and launch all three containers for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '2'
services:
api: # This is the config for the API
    image: my-cool-app-api:latest # Name of the docker image to run
    build: # Or build if it isn't available
      context: ../my-cool-app-api/ # Look here for its dockerfile
      dockerfile: Dockerfile.dev # Using this file as the dockerfile
    env_file: 
      - ../my-cool-app-api/.env # Get environment vars from this file
    volumes:
      - ../my-cool-app-api/:/app # Map the entire folder to /app
    restart: unless-stopped # Restart the container unless I stop it
    command: npm run serve # Run this command at container startup
    expose:
      - 8181 # Export port 8181
    networks:
      - my-network # Expose it to this named network

ui: # This is the config for the UI 
  image: my-cool-app-ui:latest # Name of the docker image to run
    build: # Or build if it isn't available
      context: . # Look here for its dockerfile
      dockerfile: Dockerfile.dev # Using this file as the dockerfile
    env_file: 
      - .env # Get environment vars from this file
    volumes:
      - .:/app # Map the entire folder to /app
    restart: unless-stopped # Restart the container unless I stop it
    command: npm run serve # Run this command at container startup
    expose:
      - 8627 # Expose port 8627
    networks:
      - my-network # Expose it to this named network

haproxy: # This is the config for the prooxy
  depends_on: # It depends on the following services
    - api # api, as defined above
    - ui # ui, as defined above
  image: my-haproxy:latest # Name of the docker image to run
  build: # Or build if it isn't available
    context: . # Look here for its dockerfile
    dockerfile: Dockerfile-haproxy # Using this file as the dockerfile
  ports:
    - 3000:3000 # Make port 3000 available to the host
  volumes: # Map my custom config file at runtime
    - ./haproxy-compose.cfg:/usr/local/etc/haproxy/haproxy.cfg
  restart: unless-stopped # Restart the container unless I stop it
  networks:
    - my-network # Expose it to this named network

networks:                               
    my-network: # Create a default named network for containers

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things are worth noting.&lt;/p&gt;

&lt;p&gt;The names “api” and “ui” appearing in both the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file the &lt;code&gt;haproxy.cfg&lt;/code&gt; file have to match. Docker will effectively perform runtime DNS resolution, and those names end becoming the hostnames inside the Docker-managed network.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;expose&lt;/code&gt; lines in &lt;code&gt;docker-compose.yaml&lt;/code&gt; expose those ports to the Docker-managed network, but do not expose those ports publicly to the Docker host. The &lt;code&gt;ports&lt;/code&gt; entry in the &lt;code&gt;haproxy&lt;/code&gt; configuration &lt;em&gt;does&lt;/em&gt; expose port 3000 to the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tying it All Together
&lt;/h2&gt;

&lt;p&gt;Now that I have a working &lt;code&gt;docker-compose.yaml&lt;/code&gt; file, the only thing left is to start it all up with the command &lt;code&gt;docker-compose up&lt;/code&gt;. I like the fact that all log files are sent to &lt;code&gt;stdout&lt;/code&gt; and that I can watch the progress. Once everything is up and running, I can launch a browser to &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt; to test my application. And yes, the debugging strategies detailed above for VS Code still work.&lt;/p&gt;

&lt;p&gt;The only significant piece missing is the ability to debug my API layer, as those specifics are dependent on the backend technology. The strategy will be similar, whether the API is written in Node, C#, Java, PHP, etc. You need to ensure that the appropriate debugging ports are exposed to the host, and then connect your debugger through that port as usual.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Notes
&lt;/h1&gt;

&lt;p&gt;I spent a lot of time reviewing the file diffs on the pull request I opened to implement all of these changes. In doing so, I discovered a few discrepancies, missing files, and even one unnecessary file. Typing this lengthy explanation helped me to catch and correct those oversights.&lt;/p&gt;

&lt;p&gt;And that is everything. I managed to cover every requirement and then some. The result is better and more functional than I had hoped. My only remaining hope is that my sharing this experience will help someone else save some time. If you happen to find any tweaks that make it even better, please let me know.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>angular</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
