Angular Typesafe Reactive Forms With TypeScript

(Update - March 2020 I finally created a npm package angular-typesafe-reactive-forms-helper)

Problem

Working with more complex Reactive Forms, it was challenging to remember how the form was bound to my FormGroup object.

I wanted intellisense to help me.

This is because the FormGroup class has a ‘value’ property which returns ‘any’, the cause of my frustrations.

Good luck renaming property names!

1
2
3
4
5
//my dream and challenge
get value: CustomType; // <-- somehow the angular code must return a CustomType

//from Angular FormGroup class
get value: any;

Another frustration I faced was referring to the form properties names as ‘string’ values. It quickly got out of hand when I renamed property names.

1
2
3
4
5
6
7
8
//example of usage
this.form.get('heroName').patchValue('He-Man');

//my dream and challenge
this.form.get(x => x.heroName).patchValue('He-Man');

//from Angular FormGroup class
get(path: Array<string|number>|string): AbstractControl|null

Angular team is considering implementing Typesafe Reactive Forms

Here are the issues worth tracking:
* Proposal - ReactiveForms: add AbstractControl.getChild<T> method
* Reactive forms are not strongly typed

Can’t wait for that to be fully implemented…until then, I’ll hack my way to typesafety :)

Solution

Quick Demo

The Interface used for all the code samples

1
2
3
4
5
6
interface IHeroFormModel {
name: string,
secretLairs: Array<Address>,
power: string,
sidekick: string
}

Intellisense on FormGroupTypeSafe<T>.value:

FormGroupTypeSafe.value Intellisense

Intellisense on FormGroupTypeSafe<T>.getSafe and then patching the value:

FormGroupTypeSafe.getSafe Intellisense

By having a lambda like expression instead of a ‘string’ property name, makes refactoring so much easier.

Typesafe Angular Reactive Forms Sample

I modified the Angular Reactive Forms Sample with my Typesafe Angular Reactive Forms Sample.

Search for TypeSafe Reactive Forms Changes to find all the changes. I commented out the original angular code and replaced it with the Typesafe way as a quick reference.

Files to focus on:

  • app/app.modules.ts
  • app/hero-detial.component.ts
  • app/angular-reactive-forms-helper.ts

* app/app.modules.ts *

Two things of importance here:

  • import the FormBuilderTypeSafe class
  • add to provider list for the Dependency Injection

See Typesafe Angular Reactive Forms Sample for details.

* app/hero-detial.component.ts *

  1. Define an interface of your form model.

    1
    2
    3
    4
    5
    6
    7
    //interface used with FormGroupTypeSafe<T>
    interface IHeroFormModel {
    name: string,
    secretLairs: Array<Address>,
    power: string,
    sidekick: string
    }
  2. Declare your new FormGroupTypeSafe form with the help of TypeScript’s generics.

    1
    2
    3
    4
    /* TypeSafe Reactive Forms Changes */
    //old code
    //heroForm: FormGroup;
    heroForm: FormGroupTypeSafe<IHeroFormModel>;
  3. Inject FormBuilderTypeSafe.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    constructor(
    /* TypeSafe Reactive Forms Changes */
    //old code - private fb: FormBuilder,
    private fb: FormBuilderTypeSafe,
    private heroService: HeroService) {

    this.createForm();
    this.logNameChange();
    }
  4. Create your form group with Interfaces (contracts).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // old code
    // this.heroForm = this.fb.group({
    // name: '',
    // secretLairs: this.fb.array([]),
    // power: '',
    // sidekick: ''
    // });


    this.heroForm = this.fb.group<IHeroFormModel>({
    name: new FormControl(''),
    secretLairs: new FormControl([]),
    power: new FormControl(''),
    sidekick: new FormControl('')
    });

    //***** Nested type sample *****
    interface IAddressModel {
    suburb: string;
    postcode: string;
    }

    interface ICustomerModel {
    name: string;
    address: IAddressModel
    }

    this.form = this.fb.group<ICustomerModel>({
    name: new FormControl(null, [Validators.required]),
    address: this.formBuilder.group<IAddressModel>({
    suburb: new FormControl(''),
    postcode: new FormControl('', [Validators.required]),
    })
    });

    Benefits TypeScript provide:

    • Detecting errors (contract mismatch): Let’s say we change the ‘name’ property, ‘name’ => ‘nameNew’, but the IHeroFormModel.name did not change, TypeScript will raise the alarm.

    • Renaming properties: Super easy, I’m using VSCode and it’s as easy as F2, rename the property, enter…and TypeScript will do the rest. No more search and replace.

    • No more misspelled properties: Intellisense ensures you get the correct property name everytime. With string property names a mistake can easily creep in.

    • Guidance on how to create the form group: Code sample below will have TypeScript raise the alarm when attempting to set ‘name’ to ‘’, FormGroupControlsOf<T> returns FormControl | FormGroup.

      message: ‘Argument of type ‘{ name: string; secretLairs: FormControl; power: FormControl; sidekick: FormControl; }’ is not assignable to parameter of type ‘FormGroupControlsOf‘.
      Types of property ‘name’ are incompatible.
      Type ‘string’ is not assignable to type ‘FormControl | FormGroup’.’
      at: ‘1, 49’
      source: ‘ts’

1
2
3
4
5
6
this.heroForm = this.fb.group<IHeroFormModel>({
name: '',
secretLairs: new FormControl([]),
power: new FormControl(''),
sidekick: new FormControl('')
});

5. Use the typesafe form.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//old code 
//this.heroForm.get('name').patchValue('new hero');
this.heroForm.getSafe(x => x.name).patchValue('new Hero');

//old code
//this.heroForm.setControl('secretLairs', addressFormArray);
this.heroForm.setControlSafe(x => x.secretLairs, addressFormArray);

formatAddressToOneLine(addressIndex: number) : string {
//When using angular's FormGroup no intellisense is provided
//when typing 'this.heroForm.value.'.
//The FormGroupTypeSafe form will give you intellisense.
//The benefit is when you rename property names,
//TypeScript will make sure you change all the references

//old code => this.heroForm.value.secretLairs[addressIndex] returns 'any',
//then use 'as Address' to get it to be an Address object for intellisense to work
//let address = this.heroForm.value.secretLairs[addressIndex] as Address;
//No more 'as', TypeScript knows it is an Address
let address = this.heroForm.value.secretLairs[addressIndex];
return address.street + ", " + address.city + ", " + address.state;
}

* app/angular-reactive-forms-helper.ts *

In order to make this work as closely as possible to the angular way I created an abstract class FormGroupTypeSafe<T> which derives from angular’s FormGroup.
It also ensures that exisitng code will not break.
I also created two functions: getSafe, setControlSafe.
Those were the only functions I needed for the Typesafe Angular Reactive Forms Sample.
If you need more, add them :)

You can find the implementation of angular-reactive-forms-helper.ts on my Github account or have a look at the Plunker example: Typesafe Angular Reactive Forms Sample.

Conclusion

I don’t misspell property names anymore and refactoring Reactive Forms is back to being a trivial task.
I think the extra bit of ceremony is absolutely worth it.

Use it…don’t use it :)

Build a Static Secure Blog With Hexo and AWS S3

In this post I will focus on the steps I took to make my first post publicly available as opposed to spending time on how to customise your blog.

I would highly recommend Jeff Ammons Pluralsight course on Build a Better Blog with a Static Site Generator for Hexo customisation.

Topics

Register a Domain

First thing I did was to register a personal domain name - ruanbeukes.net.

Easiest is to google ‘domain registrars’ for heaps of choices.
Choose the very first one in the list and start exploring with domain names.
Once you have identified your available domain name, you are ready to explore other registrars to compare prices.

When the price suits, and you have read a few online reviews about your new registrar, sign up to purchase your domain name.

I pay AUD$11.50 annually.

* Add Privacy Protection and protect yourself from spam and scams when purchasing your domain. *

Think of your domain name as an entry in the yellow pages where someone can lookup your phone number.
I do not want my personal details publicly available.
You can make it private by adding Privacy Protection.

I pay AUD$7 annually.

If you have concerns about your personal details, use whois and enter your domain name.

Register a AWS account to host your blog

Go to Amazon Web Services (AWS) and create a free account.
You will need a credit card to register.
Fill out your credentials and sign up for a Personal Account.

Amazon bills you when you exceed their free usage deal.
It works on a ‘Pay-As-You-Use’ model and is really cheap.
They have plenty of services to choose from but I use their S3 feature for my blog.

Create AWS S3 (Simple Storage Service) bucket

Amazon S3 gives you secure, durable and highly-scalable cloud storage.
Login to your AWS account and find the S3 service under the Services menu.

Hit the Create Bucket button and it will prompt you with a Bucket Name and Region.

* Bucket Name must be your domain name. *
The Region should be the one closest to your location.

Create your bucket!

** Permissions: **
Go to the Properties of the bucket and expand Permissions, click on the Edit bucket policy.

Add this code I found in the AWS docs

1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow Public Access to All Objects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::{YourBucketNameHere}/*"
}
]
}

** Static Website Hosting: **
Go to the Properties of the bucket and expand Static Website Hosting, enable website hosting.

Add Index Document - index.html.
Optional to add Error Document - error.html.
The Endpoint is the link you should use if you don’t have a domain name registered.
Note - The bucket endpoint is also used by CloudFlare later in this post.

** Create ‘www’ sub domain bucket and redirect to domain **
I also created a www.ruanbeukes.net bucket which points to ruanbeukes.net.

Create the www.{YourDomainName} bucket and go to its Properties and expand the Static Website Hosting. Select the ‘Redirect all requests to another host name’ option.
Fill out the ‘Redirect all requests to’ text box with the domain bucket name.

The AWS help documentation is very good. There is always a ‘Learn more’ link on the option I explored which explained in detail what the setting or section is all about.

Resource - Hosting a Static Website on Amazon S3

Install Hexo

Hexo is a static blog engine. It is a fast, simple and powerful.

Setup is easy with the Hexo Docs. Prerequisites are Node.js and Git.
Then it’s as easy as

1
$ npm install -g hexo-cli

You can make sure Hexo installed correctly by opening command prompt and run ‘hexo’.

Once installed, create a folder and initialise Hexo in that folder. By default you will get the Landscape theme.

1
$ hexo init

Install hexo dependencies.

1
$ npm install

Run the hexo server to browse your blog on http://localhost:4000/.

1
$ hexo server

Create a new post

Very easy to create a new post

1
$ hexo "Hello World"

Hexo creates a Mark Down file with a Title, Date and Tags. Now your file is ready for your content.
Check out the Hexo documentation on how to use and change the header settings.

When you’ve create your ‘Hello World’ post and you are ready to deploy to the server, I run the ‘Generate’ command.

1
$ hexo generate

It creates the output in the Public folder eg:

Run the hexo server and test your blog.

There are many more commands you can use. I only covered the ones I use regularly.

Publish to AWS S3 bucket via AWS CLI tool

Easiest way to publish files to your S3 bucket is to login to AWS, go to S3 services, use the web UI ‘Actions’ button and select ‘Upload’.
After a while it becomes a tedious repetitive job, so I researched their CLI tools - AWS CodeCommit Command Line Reference.

First download and install AWS CLI tool - Install instructions on AWS CLI tool.
When successfully installed, configure security keys.

** Configure Security Keys for CLI tool **
Go to AWS IAM (Identity and Access Management) manage console. This is where you create users and groups.

I’ve created a new user. The new user has a Access Key ID and a Security Access Key.
Keep the Secret Access Key safe as it is used for authentication to S3 buckets.

I also created a group with a Permission Policy of AmazonS3FullAccess and added the user to that group.

Then, open a command prompt and run to configure CLI security

1
2
3
4
5
6
aws configure

AWS Access Key ID [None]: AKIAJJTLECQSBDU53CGA
AWS Secret Access Key [None]: yFbx4erWVEB4zlw9uHelHqbMo6aekmtSsoGeyDcC
Default region name [None]: {YourRegionName}
Default output format [None]: json

AWS Access Key ID [None]: Type your target AWS access key ID here, and then press Enter
AWS Secret Access Key [None]: Type your target AWS secret access key here, and then press Enter
Default region name [None]: Type us-east-1 or any other region here, and then press Enter
Default output format [None]: Type json here, and then press Enter

You can test your config with ‘ls’ command. This should list all your buckets on S3.

1
aws s3 ls

Resource - Configure the AWS CLI

** Create batch file to run CLI with ‘sync’ command **
I use the ‘sync’ command to copy and update my content.
This is what I execute in the batch file

1
aws s3 sync . s3://ruanbeukes.net --delete --exclude ".vscode/*" --exclude ".git/*" --exclude "*.bat"

AWS CLI is now configured.

Resource - CLI Commands.

Create CloudFlare Account for Secure Browsing and more

I use CloudFlare for these reasons:
     1. Map my domain name to my S3 bucket endpoints
     2. Secure browsing
     3. It free! :)

The outcome of the CloudFlare setup: https://ruanbeukes.net instead of http://ruanbeukes.net.

Create a CloudFlare account.

** Map S3 bucket endpoints **
Look for the Add Site menu option and add your domain name and let CloudFlare do its magic.

Add your S3 endpoints. Here is my setup…

CNAME with my domain name S3 bucket endpoint.
CNAME with my sub domain S3 bucket endpoint.

** Secure browsing **
Next, go to the Crypto menu section and make sure your SSL is set to Flexible.

Then, create a Page Rule to force HTTPS only.

The last bit is to update your domain registrar Nameservers with the two assigned to you from CloudFlare.

One thing you need to understand about these changes - BE PATIENT! I was chasing ghost a few times because of my impatience. CloudFlare suggests up to 24hrs for settings to take effect.
In my case it was 2-3 hours. But still, I could have gone to bed earlier :)

So…when you’ve waited patiently, test your domain URL the next day or you can use WhoIsHostingThis to make sure hosting is done by CloudFlare.

CloudFlare has a CDN (Content delivery network) feature and will request my blog content from the S3 bucket.
This is the reason why the host is CloudFlare instead of Amazon.

Resource - How do I use CloudFlare with Amazon’s S3 Service?.

Done

In my next post I’ll cover adding comments to your Hexo blog.

Resources

AWS Docs

Hexo Docs

CloudFlare

Pluralsight
I would highly recommend Jeff Ammons Pluralsight course on Build a Better Blog with a Static Site Generator.
It is a goodie for Hexo customisation and other gold nuggets.

Thanks Jeff, keep up the good work!

It's Alive!!!

Welcome to my blog

I am happy to announce that my blog is finally up and running.

I’m one of those guys that Scott Hanselman would refer to as “The Dark Matter Developer”.

Well…this ends today.

I’m getting out of my comfort zone to explore a different side of me.
How else will one grow if one is not willing to try new things?

What this blog is about

I’m a Software Developer and my focus will be on web technologies.
Although I love C# and .NET, there are many other server side technologies one can use.
I believe you should use the right tool for the right job and I will share anything related to the web world.

More important, I will share my experiences.

Anything from a new cool tool I discovered and found very useful, to a really stupid thing I’ve done…

Hopefully those posts will be in the minority :)

We all make mistakes and if you can learn from mine, I’ve succeeded. Less bugs, better software.

So that’s it, my blog is officially alive.

In my next post I will share my incredible journey of building my blog.