Typescript wrongly assumes browser context

Running Typescript under NodeJS is easy

Create a new main.ts file and put some code inside it

1 console.log("Hello Typescript under NodeJS");

Install typescript compiler using

npm install typescript

Compile your code (I am assuming Windows file path style)

node_modules\.bin\tsc

And just execute it using NodeJS

node main.js

You should be aware that Typescript assumes your code is executed under a browser and therefore let you use browser specific API. For example, the following code compiles successfully but fails to execute under NodeJS

1 localStorage.setItem("key", "123");

Fortunately, we can fix that

Add a tsconfig.json file and let Typescript know the appropriate execution context

 1 {
 2   "compilerOptions": {
 3     "module": "commonjs",
 4     "target": "es5",
 5     "sourceMap": true,
 6     "lib": ["ES6"]
 7   },
 8   "exclude": [
 9     "node_modules"
10   ]
11 }

The lib section tells Typescript what external libraries it should support out of the box. In this case we just want to be able to compile some ES6 APIs

With the new configuration the Typescript compiler now generates the following error

Cannot find name ‘localStorage’

Which is exactly what we want

However, running with above configuration also fails to compile to following valid NodeJS code

1 console.log("Hello Typescript under NodeJS");

Typescript throws the following error

Cannot find name ‘console’

To fix that you need to install NodeJS typings information using the following command

npm install @types/node

Enjoy, …

Advertisements

WebStorm – Drag & Drop a reference

It took quite a long time and I am not realy sure when the WebStrom guys actualy added this feature but at last you are able to drag & drop a JavaScript file or CSS file into an HTML file and WebStrom automatically adds the appropriate syntax

For example, dropping a JS file into index.html creates the following syntax

1 <script src="app.js">script>

And dropping a CSS file creates

1 <link rel="stylesheet" href="site.css">

As a developer coming from the Visual Studio arena I think this feature is one of the most requested feature by VS developers when switching to WebStorm

Still, the feature is far from being completed

  • You cannot control the location of the reference inside the HTML file. Wherever you drop the file, the reference is always added to the header
  • Dropping  a TS file (Typescript) creates a syntax which references the TS file. In most cases we want to reference the JS file instead (same behavior for the SCSS files)

Enjoy, …

 

 

Angular2 – Minimal seed

logo

Angular2 is great. It offers much mature component based architecture than Angular 1

However, simplicity is lost.

For example, take a look at Angular2 QuickStart tutorial. You need to follow more than 10 steps in order to complete the preparation for a simple Angular2 project. These steps include configuration of NPM, Typescript, SystemJS and more. In some cases it will take more than 2 minutes just for restoring the NPM packages and those packages will hold more than 90MB of disk space consumed by more than 20,000 files !!!

For large scale project this is acceptable. For a QuickStart this is no no. Actually, I am quite surprised that Google haven’t yet make this process simpler

Of course, you can use some popular Angular2 seed like https://github.com/mgechev/angular2-seed. However, this seed is even worse, since it incorporates more technologies into the project like protractor and karma which makes the setup time even longer.

So, I decided to create a thin Angular 2 seed project. I named it angular2-min-seed and you can clone it from https://github.com/oricalvo/angular2-min-seed

Assuming you are using WebStorm you just need to “git clone” the repository, open the directory and run the main index.html file. No gulp, no “npm install”. Just open and run. The repository already contains all required WebStorm configuration for compiling Typescript files (currently, no support for others IDE)

The index.html is simple and much resemble the one for Angular1

 1 <html>
 2 <head>
 3     <title>Angular 2 QuickStart</title>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, initial-scale=1">
 6 
 7     <link rel="stylesheet" href="styles/site.css">
 8 </head>
 9 <body>
10     <my-app>Loading...</my-app>
11 
12     <script src="lib/angular.all.js"></script>
13     <script>
14         System.import('app').catch(function(err){ console.error(err); });
15     </script>
16 </body>
17 </html>

Enjoy,

logo

Angular2 – Load component’s template and styles by convention


logo

Below is a common definition of Angular2 component

clock.ts

1 @Component({
2     selector: 'clock',
3     template: require("./clock.html!text"),
4     styles: [require("./clock.css!text")]
5 })
6 export class ClockComponent {
7     constructor() {
8     }
9 }

We use the require function to load the component’s template and styles definitions.

While the code is straightforward it contains some redundant information. For example, if we decide to rename the clock.ts file to myClock.ts then we probably also rename the HTML and CSS files to myClock.html and myClock.css respectively.

Once we rename the CSS and HTML files we also need to fix our code as below

1 @Component({
2     selector: 'my-clock',
3     template: require("./myClock.html!text"),
4     styles: [require("./myClock.css!text")]
5 })
6 export class MyClockComponent {
7     constructor() {
8     }
9 }

Assuming Angular2 has support for defining template and styles by convention than the component definition could be much simpler

1 @Component({
2     selector: 'clock',
3 })
4 export class ClockComponent {
5     constructor() {
6     }
7 }

Unfortunately, Angular2 does not support that yet. Let’s do it our self. (BTW, Angular does offer a provider named ViewResolver. However, it does not support asynchronous loading of template and styles)

First, we need to override the Component decorator so each component registration is controllered by us

1 export function Component(metadata: ComponentDecoratorCtorParameters) {
2     components.push(metadata);
3 
4     metadata.moduleId = SystemJSExtensions.getExecutingModule().name;
5 
6     return function (target: Function) {
7         (<any>metadata).target = target;
8     }
9 }

Actually we are not really overriding Angular2’s Component decorator but rather define a new one. The new Component definition accepts the same options as Angular2 original decorator and stores the metadata into a global variable named components. 

Since we are just defining a new Component decorator we need to fix all components to use the new decorator

1 import {Component} from "../fx/annotations";
2 
3 @Component({
4     selector: 'clock',
5 })
6 export class ClockComponent {
7     constructor() {
8     }
9 }

Using some monkey patching we can “steal” the module URL of the current executing JavaScript file from SystemJS. Here is the the trick

 1 (function() {
 2     "use strict";
 3     
 4     var stack = [];
 5 
 6     var SystemJSExtensions = window.SystemJSExtensions = {
 7         getExecutingModule: function() {
 8             return stack[stack.length-1];
 9         }
10     };
11 
12     hook(System.constructor.prototype, "instantiate", function(original) {
13         return function(load) {
14             var promise = original.apply(this, arguments);
15 
16             hook(load.metadata.entry, "execute", function(original) {
17                 return function () {
18                     stack.push(load);
19 
20                     var res = original.apply(this, arguments);
21 
22                     stack.pop();
23 
24                     return res;
25                 };
26             });
27 
28             return promise;
29         }
30     });
31 
32     function hook(obj, name, func) {
33         var original = obj[name];
34         obj[name] = func(original);
35     }
36 })();

Once we have knowledge of all components and their module URL we can bootstrap our application by first loading all component’s template and styles and only then let Angular run its own bootstrapping logic

 1 export function bootstrap(appComponentType: Type): Promise<ComponentRef<any>> {
 2     console.log("Registered components: " + components.length);
 3 
 4     var promises = [];
 5 
 6     for(let metadata of components) {
 7         let promiseTemplate = Promise.resolve(true);
 8         if(!metadata.template && !metadata.templateUrl) {
 9             var templateUrl = new URI(metadata.moduleId).suffix("html");
10             console.log("  Loading template: " + templateUrl);
11             promiseTemplate = System.import(templateUrl + "!text").then(function (template) {
12                 metadata.template = template;
13 
14                 console.log("  Template loaded: " + template);
15             });
16         }
17 
18         let promiseStyles = Promise.resolve(true);
19         if(!metadata.styles && !metadata.styleUrls) {
20             var stylesUrl = new URI(metadata.moduleId).suffix("css");
21             console.log("  Loading styles: " + stylesUrl);
22             promiseStyles = System.import(stylesUrl + "!text").then(function (styles) {
23                 metadata.styles = [styles];
24 
25                 console.log("  Styles loaded: " + styles);
26             });
27         }
28 
29         promises.push(Promise.all([promiseTemplate, promiseStyles]).then(function() {
30             if(metadata.encapsulation === undefined) {
31                 metadata.encapsulation = ViewEncapsulation.None;
32             }
33 
34             Component(metadata)((<any>metadata).target);
35         }));
36     }
37 
38     return Promise.all(promises).then(function() {
39         return bootstrap(appComponentType);
40     });
41 }

The magic resides at lines 38-40. Only after all templates and styles were loaded and injected to the metadata object, we let Angular bootstrap the application. At that step Angular just “sees” a metadata object filled with templates and styles and does its usuall magic

Full source code can be found at the Github repository https://github.com/oricalvo/blog-angular2-template-by-convention


logo

Debugging Angular2 Source Code

Angular2 is written using Typescript and then is compiled into plain ECMA5 JavaScript.

Chrome (and other browsers) allows you to debug the Typescript straight inside the browser using JavaScript map files.

Generally, this works great. However, when you step into Angular’s code you will not see the original Typescript code but rather the compiled & bundled version of the code. This makes debugging and understanding Angular a bit harder.

To step through Angular2 Typescript source code you need to do the following

Locate the system.config.js file and change the following code

1 var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;

To

1 var setPackageConfig = packIndex;

This tells Angular to not use the bundled JavaScript files but rather load each individual source file separately. Each loaded file has source mapping embedded information which allows the browser to load the correct Typescript file.

For example, here is the content of @angular/common/index.js

 1 "use strict";
 2 function __export(m) {
 3     for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
 4 }
 5 __export(require('./src/pipes'));
 6 __export(require('./src/directives'));
 7 __export(require('./src/forms-deprecated'));
 8 __export(require('./src/common_directives'));
 9 __export(require('./src/location'));
10 var localization_1 = require('./src/localization');
11 exports.NgLocalization = localization_1.NgLocalization;
12 //# sourceMappingURL=index.js.map

The comment line is the interesting one.

After changing the setPackageConfig variable all you have to do is reload the page and start debug the Typescript files.

Have a nice debug journey …

Using relative templateUrl in Angular 2


logo

One of the annoying thing using Angular2 templateUrl is that you must use absolute URL. Else, Angular is not able to locate the template

For example, below definition does not work

1 @Component({
2     selector: 'my-app',
3     templateUrl: "./app.html",
4 })
5 export class AppComponent {
6     constructor(appRef: ApplicationRef) {
7     }
8 }

Angular has no knowledge of the current executing module’s URL and therefore is not able to resolve the templateUrl paremeter

However, CJS modules do offer the current executing module’s URL using a variable named module.id. If you are not familair with the term CJS modules don’t worry. It is just a fancy name for NodeJS modules (require, module & exports variables)

A good module loader (like SystemJS) should support the standard CJS module.id. This means that we can send Angular the current executing module’s URL and use it as the base URL for resolving all other relative URLs.

Here is the fixed code,

1 @Component({
2     selector: 'my-app',
3     moduleId: module.id,
4     templateUrl: "./app.html",
5 })
6 export class AppComponent {
7     constructor(appRef: ApplicationRef) {
8     }
9 }

The module.id value is sent to the @Component decorator. Internally, Angular uses it in order to resolve the relative templateUrl.

Of course, you must use CJS compliant module loader (like SystemJS) in order to enjoy above code. If your are importing scripts into the HTML using the plain old script tag, then above code will not work for you.

BTW, my recommendation is to prefer template over templateUrl.
templateUrl makes Angular life cycle more complex since it must download the template from the server (but this is a story for another post …)


logo

Performance killer – setTimeout

For the last few weeks I had the opportunity to analyze the performance for a large scale Angular application.

The analysis revealed the following problematic aspects

  • Holding too large DOM tree in memory
  • Running too much dirty checking
  • Executing too much AJAX requests

I am guessing you are not surprised with the results. Most applications “suffer” the same issues. Surprisingly, I also encounter a strange aspect. There were too many setTimeout invocations.

Calling setTimeout (for Angular2) or $timeout (for Angular1) means a new dirty checking. The problematic application that I analyzed initiates more than 50 setTimeout’s per one user action !!!

If you wonder why setTimeout is being used so many time, just keep reading

Lets assume the following Angular2 view (for the app component)

1 <tabset [activeTabIndex]="activeTabIndex">
2     <tab *ngFor="let tab of tabs" [title]="tab.title">
3         <span>Content {{tab.id}}</span>
4     </tab>
5 </tabset>

We use ngFor directive to create multiple tab component.

1 <div>
2     <div class="content" [ngClass]="{'active': isActive}">
3         <ng-content></ng-content>
4     </div>
5 </div>
 1 @Component({
 2     selector: 'tab',
 3 })
 4 export class TabComponent {
 5     isActive: boolean;
 6     @Input() title: string;
 7 
 8     constructor(private tabset: TabsetComponent) {
 9         this.tabset.addTab(this);
10     }
11 }

Each time a new tab component is created it informs its parent (the tabset component) about the change by invoking the method addTab

 1 @Component({
 2     selector: 'tabset',
 3     template: `<ul>
 4                     <li *ngFor="let tab of tabs" (click)="onClick(tab)">Tab {{tab.title}}</li>
 5                </ul>
 6                <ng-content></ng-content>`,
 7     styles: [require("./tabset.css!text")],
 8     encapsulation: ViewEncapsulation.None,
 9     changeDetection: ChangeDetectionStrategy.OnPush,
10 })
11 export class TabsetComponent {
12     private tabs:TabComponent[];
13 
14     constructor() {
15         this.tabs = [];
16     }
17     
18     addTab(tab:TabComponent) {
19         this.tabs.push(tab);
20     }
21 }

The tabset component updates its internal data structure and Angular does the magic of updating the DOM.

This code is quite straightforward. Lets make it even more realistic. For example, the tabset component may want to support a property named activeTabIndex. Using this property the application can set the active tab of the tabset.

 1 export class TabsetComponent {
 2     private tabs: TabComponent[];
 3     @Input() activeTabIndex: number;
 4 
 5     constructor() {
 6         this.tabs = [];
 7         this.activeTabIndex = -1;
 8     }
 9 
10     ngOnChanges(args) {
11         console.log("ngOnChanges", args);
12 
13         if(args.activeTabIndex) {
14             console.log("  activeTabIndex changed to: " + this.activeTabIndex);
15             this.select(this.tabs[this.activeTabIndex]);
16         }
17     }
18 }

And the modified app component

1 <h1>My First Angular 2 App</h1>
2 
3 <button (click)="addTab()">Add Tab</button>
4 
5 <tabset [activeTabIndex]="activeTabIndex">
6     <tab *ngFor="let tab of tabs" [title]="tab.title">
7         <span>Content {{tab.id}}</span>
8     </tab>
9 </tabset>
 1 export class AppComponent {
 2     tabs: Tab[];
 3     activeTabIndex: number;
 4     nextTabId: number;
 5 
 6     constructor(appRef: ApplicationRef) {
 7         console.log(appRef);
 8 
 9         this.nextTabId = 1;
10         this.tabs = [
11             {id:this.nextTabId++, title: "1"},
12             {id:this.nextTabId++, title: "2"}
13         ];
14     }
15 
16     addTab() {
17         this.tabs.push({
18             id:this.nextTabId++, title: "3"
19         });
20 
21         this.activeTabIndex = this.tabs.length - 1;
22     }
23 }

the addTab method adds a new entry to the tabs array and set the active tab to be the one that was just added.

Can you detect the bug?

The problem is that Angular informs the tabset component of the activeTabIndex change before a new tab component is created. Angular detect changes according to component tree, top to bottom. First it detects that the AppComponent.tabs changes. Then it detects that the activeTabIndex changes and informs the tabset component and only then it creates the new tab component.

As result, the tabset component sees an invalid activeTabIndex and ignores it

How can we fix that ?

Easily. Just add a setTimeout call and postpone the activeTabIndex change after the TabComponent is created.

Here is the fixed code,

1 addTab() {
2     this.tabs.push({
3         id:this.nextTabId++, title: "3"
4     });
5 
6     setTimeout(() => {
7         this.activeTabIndex = this.tabs.length - 1;
8     }, 0);
9 }

Running above code and everything is just great. Really ?

At first, a single setTimeout invocation feels quite minor. However, imagine a large scale Angular application where each component depends on another one. Postponing the activeTabIndex change by 0 milliseconds means that you loose control of the data flow inside your application. It is just a matter of time until you will need to introduce a second setTimeout but this time probably with a larger interval. The road eventually leads to 50 setTimeout (like the application we analyzed) and then the frustration of “Hi, my application is slow! should I switch technology?”

In most cases, the need for setTimeout is a result of bad architecture.

If you are struggling with boosting your Angular application performance then send me an email at oric@trainologic.com


logo