Check out our Angular Book Series.

Using `as type` - Type Assertions in TypeScript - Part 1

In an earlier article I wrote a bit about creating get/set properties in TypeScript. I am working on a project that uses a bunch of these, and sometimes they don't work.

Why do my get/set properties returned undefined? The method never executes, how can I fix it?

I'm going to show you why you're having problems and along the way talk about different forms of type conversions in TypeScript (and JavaScript).

The Setup

Let's start by creating a TypeScript class:


export class TestClass {
numbers: number[];
counter = 0;

get sum() {
this.counter++;
return this.numbers.reduce((accumulator, currentValue) =>
{
return accumulator + currentValue;
});
}
}

This class is named TestClass. It has three properties:

  • numbers: A regular property that contains an array of numbers
  • counter: A regular property that contains the number of the times that the sum was processed, perhaps a value you want to store for internal auditing purposes.
  • sum: A get property that returns the sum of all the values in the numbers array. Since this is a get property with no set, it is essentially read only and cannot set the value through traditional assignment.

Accessing a Get method with Simple JSON

When returning data from a server, I often get JSON, which is viewed as a generic object not tied to any specific TypeScript class. Look at code like this:


const testClassJSON = {numbers : [1, 2, 3, 4]};
console.log(testClassJSON.sum); // compile error

The first line hard codes a generic object, just like JSON that would be returned from the server to the application. this JSON matches the TestClass. But, the second line will give a compile error:

I can get this to compile if I tweak the code a bit differently:


const testClassJSON = {numbers : [1, 2, 3, 4]};
console.log(testClassJSON['sum']); // returns undefined

But, alas, that does not work, either:

The value is returned as undefined. The browser things this JSON value is a simple object:

There is no property named sum on a generic JavaScript object, and therefore cannot introspect special properties on the TestClass object.

For our get/set properties to work we need to perform some type of conversion from our generic JSON object to our formal type.

as TestClass

One version of Type assertion in TypeScript is similar to a cast that I've used in other languages.


const testClassAs = {numbers : [1, 2, 3, 4]} as TestClass;
console.log(testClassAs.sum); // returns undefined

Put this in your app and it should compile fine without any errors about the sum property not existing. That is progress. But, run the code and you see similar errors. The output to the console is undefined:

And the object in the debug tools is still seen as a generic object:

What gives? The `as type` is a TypeScript compile time assertion and has no affect at runtime. No conversion is made between your generic JSON class and your better typed TypeScript class. TypeScript does not even call this casting, because of the loosely typed nature of JavaScript. It calls it type assertion.

The angle bracket syntax of type assertion has the same problems.

For get/set properties to work, you must create an instance of that class somehow. I'm going to delve into that next week.

Understanding The JavaScript Reducer Function

Despite how long I've been creating JavaScript applications, I keep learning new things and discovering new little pieces of the language that I didn't know existed. Today I found the reducer function.

The reducer will loop over an array, with the intent of reducing the full array into a single value. The most common use case of this is to add up all the numbers in an array, like this:


console.log([1, 2, 3, 4].reduce((accumulator, currentValue) =>
accumulator + currentValue));

The reducer function accepts two arguments, the first is the accumulator and the second is the currentValue.

The first time the reduce function runs, it passes in the first two values from the array. The accumulator is the first value, 1 in this case. The currentValue is the second value,. the 2.

The second time the function is executed it returns the results of the first function run into the accumulator, and the third function. In this case, 3 is the results of the first run, and 3 is the third element in the array.

The third time the reducer is called it gets 6--the results of the third run of the function--and 4--the fourth item in the array. Then it has no more items to execute and returns the results.

Adding numbers in an array is a simple use case for this, but this could be used in much more advanced situations too, such as looping over an array of objects. Imagine you are creating a log display and want to show all the users who have edited something. Just use reducer to get the users list on each log item. Or maybe you're dealing with rights limited content and want to figure out what country the content is in; this could help if you loop over an array of 'rights metadata' objects.

Play with the code here.

Why can't Angular find the path() method on a Location object?

I'm writing some unit tests to test an Angular Router. In the test, I have code like this:


expect(location.path()).toBe('/something');

IntelliJ is giving me errors, saying it can't find the path value:

Clearly the path() method exists on the Angular Location object, so what was the cause of the error?

Apparently there is a JavaScript Location object.

The browser Location object does not contain a path() function even though the Angular one does.

Since Location exists natively, IntelliJ did not import the Angular library, and I didn't notice until I tried to run the code.

The fix is pretty simple. Just import the Angular Location library manually:


import {Location} from "@angular/common";

And you should be good to run your code and unit tests without any errors.

Creating Angular Directives Part 5 - Applying One Directive to Different Elements

I'm updating my Learn With tutorial books that teach Angular Development for Angular 8. I decided to include a deep dive into Angular Directives. This series of posts is a snippet of what will be in the full series, and is focused on creating attribute directives.

Check out Part 1, with project setup and a starter directive and part 2 that introduces the renderer2, and part 3 that discusses input properties, and Part 4 which covers event dispatching.

We have created a directive named reverse which will toggle CSS Between two separate states.

Using the Directive Multiple Times

All our samples so far have been applying the styles to the button, but we can easily use this same directive on other elements; here it is in a p tag and a div:


<p [appReverse]="reversedState"
regularCSSClass="regular"
reversedCSSClass="reversed">

The directive in a p!
</p>
<div [appReverse]="reversedState"
regularCSSClass="regular"
reversedCSSClass="reversed">

The directive in a div!
</div>

The button is still in the view, and can be used to toggle:

Click the button and everything is reversed:

This is where we start to see the real power of encapsulating this in a directive. We are now writing simpler code than we would have if we were trying to do this using ngClass. Let's add four new styles into the create-attribute-directive.component.css:


.regular-red {
background: #ffffff;
color: #ff0000;
}

.reversed-red {
background: #ff0000;
color: #ffffff;
}

.regular-blue {
background: #ffffff;
color: #0000ff;
}

.reversed-blue {
background: #0000ff;
color: #ffffff;
}

The styles use red and white, or blue and white instead of our usual black and white. Apply them to HTML elements in create-attribute-directive-component.html:


<p [appReverse]="reversedState" regularCSSClass="regular-red" reversedCSSClass="reversed-red">
The directive in a p!
</p>

<div [appReverse]="reversedState" regularCSSClass="regular-blue" reversedCSSClass="reversed-blue">
The directive in a div!
</div>

The red style is applied to the p tag and the blue styles are applied to the div. Rerun the code:

And click to switch:

Final Thoughts

The bulk of Angular development I do is focused on creating custom components, not custom directives. But, understanding directives is important to being a good Angular developer because they give a different sort of flexibility that can be a benefit.

Be sure to check out my Learn With Series on Angular 8, which will cover this example in more detail, and even teach you all about Structural directives.

Creating Angular Directives Part 4 - Dispatching Events

I'm updating my Learn With tutorial books that teach Angular Development for Angular 8. I decided to include a deep dive into Angular Directives. This series of posts is a snippet of what will be in the full series, and is focused on creating attribute directives.

Check out Part 1, with project setup and a starter directive and part 2 that introduces the renderer2, and part 3 that discusses input properties.

We have created a directive named reverse which will toggle CSS Between two separate states.

Dispatching Events

A directive can dispatch its own events, just like a component can. Let's add an event to the directive that will dispatch whenever the state changes. First, I'll add a new class:


export class CSSReversedEvent {
addedCSSClass: string;
removedCSSClass: string;
}

I put this class in the reverse.directive.ts file, instead of creating its own class file. It includes one value for the CSS Class that was removed, and one for the CSS Class that was added.

Now inside the ReverseDirective class, add the output event:


@Output() stateChange: EventEmitter<CSSReversedEvent> = new EventEmitter();

This uses the @Output() metadata to create the EventEmitter named stateChange. How look at the changeState() method:


changeState() {
const newState: CSSReversedEvent = new CSSReversedEvent();
if (this.reversedState) {
this.renderer.removeClass(this.el.nativeElement, this.regularCSSClass);
this.renderer.addClass(this.el.nativeElement, this.reversedCSSClass);
newState.addedCSSClass = this.reversedCSSClass;
newState.removedCSSClass = this.regularCSSClass;
} else {
this.renderer.removeClass(this.el.nativeElement, this.reversedCSSClass);
this.renderer.addClass(this.el.nativeElement, this.regularCSSClass);
newState.addedCSSClass = this.regularCSSClass;
newState.removedCSSClass = this.reversedCSSClass;
}
this.stateChange.emit(newState);

The first line in the method creates a new instance of the CSSReversedEvent class. The middle of the method will add the CSS Values onto this new object, depending on which state we are entering. The final name of the method will dispatch the event.

Open up the create-attribute-directive.html file and add in the stateChange event listener:


<button (click)="reverseState()"
[appReverse]="reversedState"
regularCSSClass="regular"
reversedCSSClass="reversed"
(stateChange)="onStateChange($event)">

Reverse State
</button>
The event listener will call the onStateChange() event inside the create-attribute-directive.component.ts file:


onStateChange(event) {
console.log(event);
}

For simplicity I just outputted the event to the console.

Rerun the code and start clicking buttons:

During component initialization this event is dispatched twice. Once after the reversedState value is set, but before the regularCSSClass and reversedCSSClass are set. We see the event dispatching undefined for each class in this case. Then it is dispatched a second time after the initialization is complete. This is when the initial styles are applied to the button.

Click the button a few times to see the events continue to dispatch:

At this point in the series, you should realize that there is a lot you can do with directives.

What's Next?

For the last article in this series, I'm going to give a sample of applying the same directive to different elements.

Creating Angular Directives Part 3 - Exposing Properties

I'm updating my Learn With tutorial books that teach Angular Development for Angular 8. I decided to include a deep dive into Angular Directives. This series of posts is a snippet of what will be in the full series, and is focused on creating attribute directives.

Check out Part 1, with project setup and a starter directive and part 2 that introduces the renderer2.

We created a directive named reverse which will toggle CSS Between two separate states.

Expose New Properties

The directive as is is hard coding the style values inside the directive. That is horrible use of encapsulation, and limits reuse. Let's open those up. Open up reverse.directive.ts:


@Input() regularCSSClass: string;
@Input() reversedCSSClass: string;

I added one input for the regularCSSClass and one for the reversedCSSClass. Now move to the changeState() function:


changeState() {
if (this.reversedState) {
this.renderer.removeClass(this.el.nativeElement, this.regularCSSClass);
this.renderer.addClass(this.el.nativeElement, this.reversedCSSClass);
} else {
this.renderer.removeClass(this.el.nativeElement, this.reversedCSSClass);
this.renderer.addClass(this.el.nativeElement, this.regularCSSClass);
}
}

Instead of accessing hard coded values as the CSS Class is added, or removed, from the native element the variables are added.

Notice how the values have no default styles. To solve that we'll make sure to call the changeState() method when ngOnInit() runs, which will be after the property values are set. First implement the OnInit interface as part of the class definition:


export class ReverseDirective implements OnInit {

Then add the ngOnInit() method:


ngOnInit() {
this.changeState();
}

This will make sure that the default states are set after the component is initialized. Open up create-attribute-directive.component.html and add in our two new properties:


<button (click)="reverseState()"
[appReverse]="reversedState"
regularCSSClass="regular"
reversedCSSClass="reversed">

Reverse State
</button>

Angular's magic under the hood will know the new attributes belong to the directive and will pass in those values.

Rerun the app:

Then click the button:

The new Angular directive inputs were set up as regular HTML attributes with string values, but we could set them up as Angular attributes with an Angular expression as the value. In the simplest form, like this:


<button (click)="reverseState()"
[appReverse]="reversedState"
[regularCSSClass]="'regular'"
[reversedCSSClass]="'reversed'">

Reverse State
</button>

The Angular expression is a literal string, and the regularCSSClass and reversedCSSClass are enclosed in brackets, signifying they are properties.

What's Next?

The next part of this series will show you how to use events as part of a directive.

Creating Angular Directives Part 2 - Using Renderer2

I'm updating my Learn With tutorial books that teach Angular Development for Angular 8. I decided to do a deep dive into Angular Directives. This series of posts is a snippet of what will be in the full series, and is focused on creating attribute directives.

Check out Part 1, with project setup and a starter directive. We created a directive named reverse which will toggle CSS Between two separate states.

Using Renderer

If you read the docs on the ElementRef's nativeElement property, there is a big warning to avoid using it. It breaks encapsulation by accessing the DOM directly, and can lead to unexpected results in the application.

We're going to use the Renderer2 component instead. It is designed to give a layer of abstraction between Angular and the DOM within directives like this. While we're at it, I'm also going to switch to using a CSS Class instead of accessing styles directly.

The first step is to inject the Renderer2 into the directive's reverse.directive.ts file:


constructor(private renderer: Renderer2, private el: ElementRef) { }

The reversedState property remains unchanged, but we will want to modify the changeState() method. This is the current method:


changeState() {
if (this.reversedState) {
this.el.nativeElement.style.backgroundColor = '#000000';
this.el.nativeElement.style.color = '#ffffff';
} else {
this.el.nativeElement.style.backgroundColor = '#ffffff';
this.el.nativeElement.style.color = '#000000';
}
}

This uses the nativeElement to change the DOM directly. Widely considered a bad a practice.

Change it to use the renderer2 to add and remove classes:


changeState() {
if (this.reversedState) {
this.renderer.removeClass(this.el.nativeElement, 'regular');
this.renderer.addClass(this.el.nativeElement, 'reversed');
} else {
this.renderer.removeClass(this.el.nativeElement, 'reversed');
this.renderer.addClass(this.el.nativeElement, 'regular');
}
}

Instead of drilling down into the nativeElement directly, we're calling the addClass() and removeClass() methods on the renderer, and passing in the nativeElement along with the new style. We could have done the same thing with the previous sample, using addStyle() and removeStyle() methods on the renderer.

Let's be sure to add our regular and reversed styles to create-attribute-directive.css:


.regular {
background: #ffffff;
color: #000000;
}

.reversed {
background: #000000;
color: #ffffff;
}

Rerun the code:

Then click the button:

There are no change from the previous implementation, we just have a different, better, implementation under the hood.

What's Next?

Part 3 of this series will show you how to use properties in your component so that you do not have to hard code styles inside it.

Creating Angular Directives Part 1 - A Simple Directive to Toggle State

I'm updating my Learn With tutorial books that teach Angular Development for Angular 8. I decided to do a deep dive into Angular Directives. This series of posts is a snippet of what will be in the full series, and is focused on creating attribute directives.

What is an Angular Directive?

An Angular directive is just like an Angular component, except with no template. A directive can be applied to any HTML Tag or template. There are two types of directives:

  • Attribute: An Attribute directive will apply something to an existing HTML Element, or even your own Angular component.
  • Structural: A Structural directive will modify the HTML body by creating, or removing, elements.

This series of posts will focus on creating an attribute directive.

Setup

First, create a new project with the Angular CLI:


ng new

You can follow through the instructions at your discretion, but you'll go through a wizard like this:

I named the project ud, for Understanding Directives. I set up routing and used regular CSS.

In the app.component.html file, delete everything except the router-outlet, so the file looks like this:


<router-outlet></router-outlet>

You can run the app, but you won't see much until we start adding routes.

Create the Attribute Directive

We're going to create a directive that will toggle an HTML Element between two separate visual states, done by changing different CSS elements on the directive.

First, we'll do some setup. Create a component for creating our own attribute directive:


ng generate component create-attribute-directive

You'll see something like this:

Open up the app-routing.module.ts file and a sample2 route to the Routes array:


const routes: Routes = [
{path: 'sample2', component: CreateAttributeDirectiveComponent},
{ path: '**', redirectTo: '/sample2'}
];

Now, use the Angular CLI to create our first directive:


ng generate directive directives/reverse

You'll see something like this:

I put the new directive in the directive directory. If nothing is specified it will be put in src/app by default, but long term I like to keep my code better organized without a lot of things in the main app directory. Open up the reverse.directive.ts:


import { Directive } from '@angular/core';
@Directive({
selector: '[appReverse]'
})
export class ReverseDirective {
constructor() { }
}

First, we import the Directive class. This is metadata used to define the directive. The only thing we define, at this point in the metadata is the selector. We'll use this when we apply the directive to an HTML element, the Angular module, something like this:


<button appReverse></button>

Right now, the directive doesn't do anything, but we'll come back to this in the HTML template later. It is strongly recommended you do not name your custom directives with a prefix ng because that is used by the Angular framework and we want to avoid conflicts with other existing directives.

I want to note that the directive is loaded in the main Angular module. Open up app.module.ts and you'll see it in the declarations section:


@NgModule({
declarations: [
AppComponent,
UseAttributeDirectiveComponent,
CreateAttributeDirectiveComponent,
ReverseDirective
],
// other stuff
})

If you do not add your directive to the @NgModule your app won't know how to find it and you'll get compiler errors.

The first thing we want to do is inject the ElementRef into the directive class:


constructor(private el: ElementRef) { }

Dependency Injection for Directives works just like it does for other components, so we inject the value into the constructor. This will give the directive class a reference to the component which is was placed on.

This directive needs to pass in a value of the directive name, and then make changes to the ElementRef's styles based on that. To do that we'll need to define an Input() metadata. The simplest way to do this:


@Input('appReverse') reversedState: Boolean;

When the directive sees the appReverse directive applied to an HTML tag as an attribute, the value will be set in the reversedState value inside the directive class. In order to run some code whenever that value changes I'm going to switch it to a get/set method:


private _reversedState = false;
@Input('appReverse')
get reversedState() {
return this._reversedState;
}
set reversedState(input) {
this._reversedState = input;
this.changeState();
}

The get and set methods are pretty standard. A private variable holds the state. The get method will return that private variable, and the set method will set it. The set method also runs a changeState() method, so let's look at that:


changeState() {
if (this.reversedState) {
this.el.nativeElement.style.backgroundColor = '#000000';
this.el.nativeElement.style.color = '#ffffff';
} else {
this.el.nativeElement.style.backgroundColor = '#ffffff';
this.el.nativeElement.style.color = '#000000';
}
}

This method checks the reversedState value and sets the background and foreground colors. I use nativeElement property on the ElementRef to drill down into the component and set specific styles. For the moment, I'm hard coding the color values, but we'll revisit that in a later sample.

How do we use the directive? Open the create-attribute-directive.component.html file:


<button (click)="reverseState()" [appReverse]="reversedState">
Reverse State
</button>

The appReverse directive is placed directly on the button; and it is given the value of reversedState, which is a Boolean value in the component that we'll create. The click button is used to call a reverseState() method that will toggle the reversedState property.

Open the create-attribute-directive.ts file. First create the reversedState property:


reversedState = false;

Then add the reverseState() method:


reverseState() {
this.reversedState = !this.reversedState;
}

We've seen this method, and property, set up identically in the previous sample. The major change is in how the new styles are applied to the button.

Rerun the app:

Click the button and the state should switch:

Play with it to switch the state back and forth.

Congratulations you did it!

What's Next?

While this is a simple example, it does show that we can create our own directive and use it to affect the HTML element is is applied to. This type of example is pretty common when you search for directive tutorials throughout the web, however by accessing the nativeElement directively it creates a dependency between your directive and the internals of Angular which may change.

The next article in this series will show you how to address this using the Angular renderer.

How do I redirect to a different domain inside Angular?

I was doing a code review for a colleague,and as part of his code he was redirecting the browser:


window.location.href = "http://www.google.com";

This code was working perfectly fine, but when performing a redirect to another domain in an Angular component, there is a `more angular` way to make this work by using the Document service.

First import this:


import {DOCUMENT} from '@angular/common';

Then you need to inject that into your component:


constructor(
@Inject(DOCUMENT) private document: Document
) {}

This inject syntax is a bit different than a normal Angular service because it uses the Inject metadata.

Once the document is injected, you can redirect like this:


this.document.location.href = 'http://www.google.com';

This is just an Angular layer of abstraction over the native JS way to perform a redirect; but when working within an Angular component I like to lean on the Angular abstractions. They help with unit testing purposes.

Get the code here.

How do I change the URL in an Angular Application without reloading the Route?

I'm working on an Angular app, as I often do and wanted to change the the route in the URL without reloading the application. In essence, the user has just created something, we wanted to redirect the user to the edit screen, with all the data loaded. The catch was that the edit screen and create screen were the same exact component, just with different data loaded. And we didn't need to load new data because the user just entered everything; and any database specific data like a primary key was just returned from the service. How can you do that? Use the the Location lib from the Angular library. I'm going to show you how.

The Setup

First, create a new Angular application with routing. Then create a view component--I Called mine view1.

Change the main app.component.html to contain the router outlet:


<router-outlet></router-outlet>

And add these routes to the routing module:


const routes: Routes = [
{
path: 'new',
component: View1Component
},
{
path: 'edit',
component: View1Component
}
];

We're going to load up the new route, and then redirect to edit on the click of a button.

Redirect

In view1.component.html, add a button:


<button (click)="onSwitch()">Change URL</button>

When this button is clicked, we'll perform the URL change without loading the new route.

Move to the view1.component.ts file. First import the Location from @angular/common:


import { Location } from '@angular/common';

This is important, because for some reason IntelliJ is not importing the Location object properly, and nothing works if the import is missing, or worse lost.

Location is an Angular service, so inject it into the view1 component:


constructor(private location: Location) { }

Now, in our onSwitch, use the replaceState method:


onSwitch() {
this.location.replaceState('/edit');
}

This should work:

Play with the sample here.

More Entries

All Content Copyright 2005, 2006, 2007, 2008, 2009 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.002.