Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Joel Hill and Matt Vickers and Shawn Grigson and Jonathan Rowny and Jonathan Dowdle and Christian Ready and Oscar Arevalo and Jeff McDowell and Steve 'Cutter' Blades
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Joel Hill@Jiggidyuo ) , Matt Vickers@envex ) , Shawn Grigson@shawngrig ) , Jonathan Rowny@jrowny ) , Jonathan Dowdle@jdowdle ) , Christian Ready@christianready ) , Oscar Arevalo@oarevalo ) , Jeff McDowell@jeff_s_mcdowell ) , and Steve 'Cutter' Blades@cutterbl )

Using NgOnChanges Collection With Dot-Notation And TypeScript In Angular 2.4.4

By on

A couple of months ago, I looked at using dot-notation vs. array or bracket notation with TypeScript objects that have generic key-value interfaces. In that post, I demonstrated that you needed to use array-notation or the TypeScript transpiler will complain. One of the places in which this has continued to be a nuisance is the ngOnChanges() life-cycle method in Angular 2 components. Lately, however, I have started sub-classing the SimpleChanges interface in order to allow for the more natural dot-notation.

If you look at the SimpleChanges interface in the Angular 2 source code, you can see that it uses a generic key-value map:

export interface SimpleChanges { [propName: string]: SimpleChange; }

This generic interface forces us to use array-notation / bracket-notation in our ngOnChanges() life-cycle method. So, for example, if I had an Angular 2 component that accepted an Input binding, the use of dot-notation when inspecting the ngOnChanges() argument would throw an error:

// Import the core angular services.
import { Component } from "@angular/core";
import { OnChanges } from "@angular/core";
import { SimpleChanges } from "@angular/core";

@Component({
	moduleId: module.id,
	selector: "my-widget",
	inputs: [ "value" ],
	template:
	`
		<strong>Value</strong>: {{ value }}
	`
})
export class MyWidgetComponent implements OnChanges {

	public value: string;

	// I initialize the emoticon button component.
	constructor() {

		this.value = "";

	}

	// I get called whenever the bound inputs change (including the first binding).
	public ngOnChanges( changes: SimpleChanges ) : void {

		if ( changes.value && ! changes.value.isFirstChange() ) {

			console.log(
				"Value change from",
				changes.value.previousValue,
				"to",
				changes.value.currentValue
			);

		}

	}

}

Here you can see that I am declaring the ngOnChanges() argument as type SimpleChanges. And, when I try to use dot-notation to inspect the "changes.value" property, TypeScript logs the following error:

Property 'value' does not exist on type 'SimpleChanges'. (TS2339)

To get around this problem, I've started to sub-class the SimpleChanges interface within the component, explicitly defining the input properties that I expect to consume:

interface InputChanges extends SimpleChanges {
	value?: SimpleChange;
}

This creates a new interface that extends the generic key-value mapping of the SimpleChanges interface, but also provides for an optional and explicit ".value" property. Now, in my Angular 2 component, I can use this interace when defining my ngOnChanges() life-cycle method:

// Import the core angular services.
import { Component } from "@angular/core";
import { OnChanges } from "@angular/core";
import { SimpleChange } from "@angular/core";
import { SimpleChanges } from "@angular/core";

interface InputChanges extends SimpleChanges {
	value?: SimpleChange;
}

@Component({
	moduleId: module.id,
	selector: "my-widget",
	inputs: [ "value" ],
	template:
	`
		<strong>Value</strong>: {{ value }}
	`
})
export class MyWidgetComponent implements OnChanges {

	public value: string;

	// I initialize the emoticon button component.
	constructor() {

		this.value = "";

	}

	// I get called whenever the bound inputs change (including the first binding).
	public ngOnChanges( changes: InputChanges ) : void {

		if ( changes.value && ! changes.value.isFirstChange() ) {

			console.log(
				"Value change from",
				changes.value.previousValue,
				"to",
				changes.value.currentValue
			);

		}

	}

}

This time, when I run the Angular 2 application, everything works perfectly; TypeScript doesn't complain about my use of "changes.value" because the newly sub-classed interface defines an explicit ".value" property.

TypeScript definitely takes some getting used to. But, the more I use it, the more I like it. It really forces me to think about what data I can count on; and, what data may exist, but is unsafe to reference. Sub-classing the SimpleChanges interface adds explicitness to my Angular 2 components and allows me to use dot-notation without error.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

The site contains a very great blog. the information present in this site will be very useful for us. thank you for sharing the blog with us.

2 Comments

A really good idea. Thanks for this post!

For my part, I love TypeScript even more than JavaScript. Quality and readability is so much higher with typings.