Now that Angular 2 is out and given the importance of Typescript in the Angular 2 ecosystem, it’s time to understand how to create a proper building system for our projects.
If you see any Angular 2 code example out there, you’re going to see an extensive use of the import statement to load modules into our code like in the example below:
import {Component} from 'angular2/core';
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
export class AppComponent { }
This code example is extracted from the Angular 2 website. It doesn’t really matter what this code does, what matters is that in the very first line they are importing the class Component to use it in the code. So the first question that comes to my mind is: can we run this code directly in our browsers without any building process? The answer is Yes, we can if we use SystemJS.
SystemJS
Lets start this example code by creating a folder for our test project that we are going to call systemjs.
$ mkdir systemjs
$ cd systemjs
Our next step is to create two simple files that we are going to call main.ts and person.ts.
export class Person {
public name: string = 'David';
}
In person.ts we are just defining a simple class using the ES6 syntax that we are going to be using in main.ts.
import {Person} from './person.ts';
let person = new Person();
console.log(person.name);
As we said before, in order for our import statements to work, we are going to need SystemJS. We can obtain this library from npm, but before doing that, we need to create a package.json file with default values to create a list with all of our project’s dependencies.
$ npm init --force
Now, we are ready to download SystemJS.
$ npm install systemjs --save
Because SystemJS is going to handle all of the dependencies of our code, every time that one of our files import a class, a function, a variable etc., from another file, SystemJS is going to do a call to the server to get that dependency.
In our case, main.js is importing a class from person.js, so SystemJS before serving the file main.js is going to do a XHR call to the server to get person.js first. It’s now obvious that we are going to need a web server for our app, and in this case we are going to be using the npm package lite-server.
$ npm install lite-server --save
After installing the package, we can start the web server.
$ lite-server
> zsh: command not found: lite-server
What happened? Turns out that when trying to invoke the lite-server package like that, our system is trying to lookup the PATH variable where all of our executable files live but in our case, the lite-server package lives inside of our own node_modules folder which is not by default in the PATH. We can fix that by creating a “script” inside package.json, that way, node is going to add by default our node_modules folder in the PATH and it’s going to be able to find the appropriate package.
{
"name": "systemjs",
"version": "1.0.0",
"description": "",
"dependencies": {
"lite-server": "^2.1.0",
"systemjs": "^0.19.22",
},
"scripts": {
"dev": "lite-server"
},
"author": "",
"license": "ISC"
}
Now we can run the server with the command
$ npm run dev
Loading the Dependencies in the Browser
With our dependencies in place, let’s move on and create our index.html file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SystemJS</title>
<script src="node_modules/systemjs/dist/system.js"></script>
<script>
System.config({
transpiler: 'typescript'
});
System
.import('main.ts')
.then(null, console.error.bind(console));
</script>
</head>
<body>
</body>
</html>
In line 6 we are loading the SystemJS in the browser. In lines 8-10 we are configuring SystemJS to use typescript as the transpiler for our code (the transpilation is going to be performed at runtime in the browser). Finally, in lines 11-13, we are importing the entry point of our application, in this case the file main.ts.
We are ready now so we can start the web server with the command npm run dev, open our browser and go to the address localhost:3000. If we open the browser’s console, we can see that something went wrong with SystemJS.
> SyntaxError: Unexpected token <(…)
Whats missing? We told SystemJS that we are going to use typescript as the transpiler for our code but we didn’t give the browser access to the transpiler to perform the job. To fix that, we need to install the typescript transpiler locally using npm.
$ npm install typescript --save
Then we need to go back to index.html and load the library.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SystemJS</title>
<script src="node_modules/systemjs/dist/system.js"></script>
<script src="node_modules/typescript/lib/typescript.js"></script>
<script>
System.config({
transpiler: 'typescript'
});
System
.import('main.ts')
.then(null, console.error.bind(console));
</script>
</head>
<body>
</body>
</html>
At this point if we reload the browser, we are going to be able to see the string “David” in the browser’s console.
There’s one minor detail that we should fix. If we take a closer look at the file main.ts, we can see that we are defining the extension (.ts) of the dependency person.ts
import {Person} from './person.ts';
let person = new Person();
console.log(person.name);
When working in node, we usually don’t specify the extension of a module so to be consisten in the frontend, we should be doing that import like:
import {Person} from './person';
To accomplish that, we need to configure SystemJS to use the .ts extension as the default. If we see the SystemJS config API we can notice that such property exists but for some reason, we are now forced to put our typescript code inside another folder, it can’t be directly in the root folder. Although the reason to enforce that is not clear to me, having a separate folder for our “source” code is actually a good practice. So we can stop protesting against the SystemJS convention and go ahead and create a new folder called src, and move the files main.ts and person.ts to that folder.
$ mkdir src
$ mv main.ts src
$ mv person.ts src
Our now folder structure is as follow:
.
├── index.html
├── package.json
└── src
├── main.ts
└── person.ts
We can now update our index.html file to define the default extension and load the main.ts file from it’s new location.
<!DOCTYPE html>
<html>
<head>
<title>SystemJS</title>
<script src="node_modules/systemjs/dist/system.js"></script>
<script src="node_modules/typescript/lib/typescript.js"></script>
<script>
System.config({
transpiler: 'typescript',
packages: {
src: {
defaultExtension: 'ts'
}
}
});
System
.import('src/main')
.then(null, console.error.bind(console));
</script>
</head>
<body>
</body>
</html>
In lines 10-13 we are defining the default extensions of our files using the package property. Notice that the keys right under the package property should match any of our folders, in this case we are referencing our src folder.
On line 17 we are changing the location of our main file and deleting the explicit mention to the .ts extension. Similarly, we can modify our main.ts file to delete the extension of the dependency.
import {Person} from './person';
let person = new Person();
console.log(person.name);
If we go back to see our browser’s console we can see that everything is working as expected with a more concise import syntax.
I highly recommend JSPM which is a sub project of system.js its way more powerful
Checkout the Ultimate Angular 2 Boorstrap App: @ http://ng2.javascriptninja.io
Source@ https://github.com/born2net/ng2Boilerplate
Regards,
Sean
================
Thanks for the recommendation Sean. I’m actually trying to decide the best build system for an angular 2 project with typescript. Right now I’m exploring webpack as well to see if it’s potentially simpler to implement than gulp + systemjs. I will take a look at jspm for sure.
nice article
Thanks 🙂
nice, thanks for the article! How do you bundle all the source to a single file for production?
For that you might have to use webpack instead of systemjs
I also noticed when I tried this out yesterday, that typescript.js is nearly 4 megabytes in size! That’s simply too large in my opinion. I know that tsc (typescript compiler) can output JS files compatible with the SystemJS module system. If the TS is pre-transpiled, I assume we wouldn’t need include typescript.js… do you know how to bootstrap such files? I’ve been trying this all day to no avail.
You’re right, this is a build configuration only for development, not production.
Why bundling? Sole purpose of SystemJS is that it can read modules *as they are*.
You can load CommonJS, ES6 and AMD.
Inability of doing so spawned the need of bundlers into-one-massive-production file.
If you have a large project then there could be hundreds of network calls to load all of the js files. This is unacceptable in production over possibly a mobile connection.
If using http2 this is less of a problem as many small network requests are much cheaper.
For someone learning TypeScript, angular2 and everything in between, I’ve been racking my brain around module loading. This article goes a long way to clear things up (at least with SystemJS).
Thx
Glad you find it useful 🙂
Thanks! This was very useful. I’ve been reading ng-book 2 and found systemjs and modules very confusing
This is very useful and appreciate the way it was put together. In continuation to this tutorial, I like to see generating ‘build’ for production using systemJS. Look forward to it.
Thanks
[…] Source: How to Use Typescript with SystemJS ← David Barreto […]
In a typical example (found on internet)
System.config({
packages: {
app: {
format: ‘register’,
defaultExtension: ‘js’
}
}
});
The “app” folder is storing source files with extension “js”. Could you tell me about “register” property there
To be honest with you I don’t know. Since I wrote this post I started using webpack instead of systemjs and never looked back.
Hi,I was trying the same in visual studio but ended up in getting system is undefined error.I have copied the system,js content in URL(https://npmcdn.com/systemjs@0.19.31/dist/system.js) to my local file system.js and referred it in my index.html.
Whereas this approach works well in plunker. Anyone tried this appraoch in visual studio
I don’t know exactly why is failing for you but I can tell you that my IDE of choice when working with these technologies is Visual Studio Code and it’s amazing.
How to config external modules ? like ‘rxjs’ . I mean how to tell System loader to find external library file in System.config ?
did you mean npm init –force instead of npm install –force ?
You’re right. Updated, thanks
I must tell you that this one is really nice article…. I have just started anuglarjs2, I was looking how initial process is working and this article give me the proper answer.
A nice concise article but I would be interested in how you then productionise this.
I suppose all you need to do is remove the tsc script tag and change the default extension.
Do you have a different index.html for this or modify the same one with some sort of build job?
Great job, so nice to read a well written tutorial every now and then!
Very good article and well written. But this is now a year old, and we don’t really transpile Typescript in the browser anymore (even for development/testing). Maybe the author can look at updating the acticle? Cheers.
Thanks for the article, very useful information!
I figured you can define the package as “./” then you are not forced to use the “src” folder, as in the following example:
System.config({
transpiler: ‘typescript’,
packages: {
“./”: {
defaultExtension: ‘ts’
}
}
});
System
.import(‘./main’)
.then(null, console.error.bind(console));
This is a great article, it helped me understand well, thanks for the detailed explanation.
Hi, The above example is not working for me, Are you sure it is correct and updated to new version of typescript?
Typescript is ok. SystemJS is not. The example doesn’t work with the versions 2.0.x of SystemJS. You need to stay with version 0.19.x, e.g.:
npm install –save systemjs@0.19.47
The example works fine with the most recent stable versions of the other packages.
@somups if you really need SystemJS 2.0, then:
– a transpiler plugin must be specified (https://github.com/systemjs/systemjs/releases/tag/0.20.0), e.g. plugin-typescript:
npm install –save systemjs@0.20 plugin-typescript
– Update index.html:
SystemJS 2.0 and transpiling inside the browser
http://node_modules/systemjs/dist/system.js
System.config({
warnings: true,
map: {
‘typescript’: ‘node_modules/typescript/lib/typescript.js’,
‘ts’: ‘node_modules/plugin-typescript/lib/plugin.js’
},
transpiler: ‘ts’,
packages: {
‘src’: {
main: ‘main’,
defaultExtension: ‘ts’
}
},
meta: {
‘typescript’: {
“exports”: “ts”
}
}
});
System
.import(‘src’)
.then(null, console.error.bind(console));
That’s it. It’s working with lite-server (npm run dev) but even as … a static page (e.g. with FF: firefox index.html)
Latest versions of system.js use a plugin as the default transpiling method, so the config will be a bit different.
I suggest taking a look at system.js GitHub page for info on how to get it working
https://github.com/frankwallis/plugin-typescript
Good luck
No worked(((
[…] will have to use a bundler (Webpack [the documentation here], Browserify) or a loader (SystemJS [a tutorial here], RequireJS) and to configure TypeScript with this […]