ponyfoo.com

Setting up an Angular 2 Development Environment

Fix

Angular 2 is the rave of the moment. It is a modern JavaScript framework that is supercharged with awesome features. In fact, Angular 2 is now more than a framework, it is a platform for developing web, mobile and desktop applications. It is a complete re-write of Angular 1 that takes advantage of the new ES6 features, TypeScript, server-side rendering, RxJS and the goodies that modern JavaScript has to offer.

Today we have a sponsored article from Cloudinary, where Prosper writes about setting up an Angular 2 development environment with all the different tools that are available to its ecosystem. You’ll learn a bit about TypeScript, SystemJS, and webpack.

Cloudinary is an image and video management solution on the cloud for front-end developers, and I’m super excited that they’re sponsoring us! ☁️🖼

Angular 2 requires a bit of setup to get started. To avoid the headaches associated with setup, the Angular team came up with the Angular CLI. The Angular 2 CLI makes it easy to create an application that just works out of the box.

Install the Angular 2 CLI globally:

npm install -g angular-cli

Note: The Angular team has decided to drop the 2 from the name. So, it is now called Angular instead of Angular 2. For the sake of this tutorial, I’ll use Angular 2 to prevent confusion from developers just trying out the framework for the first time.

Use these commands to simply create your app and run it:

ng new myapp // creates a new app
ng generate // generates components, routes, services and pipes
ng serve // serves your application in the browser

In this tutorial, we’ll avoid using the CLI and learn how to set up our development environment from scratch. Meanwhile if you are interested in using the CLI with Cloudinary, check out this great sample. Should we use SystemJS? Is Webpack the best option? How does the transpiling work? You’ll get the answers to these questions as we get our hands dirty with the nitty-gritty of setting up and Angular 2 application.

Set up Your Base Project

Quickly go ahead and create a new directory, newapp. Move into the directory and create an index.html.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Angular 2 app</title>
</head>
<body>
  <h2>Setting up my development environment</h2>
</body>
</html>

Create another file, package.json by running the npm init command from the terminal. You can simply just type enter several times throughout all the questions been asked to speedily create the file.

Let’s install a package, lite-server, that will allow us to serve our application like so:

npm install --save-dev lite-server

Note: lite-server comes bundled with browser-sync which automatically reloads the browser when our files change.

Open up package.json to configure lite-server like so:

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "lite": "lite-server"
}

Now run npm run lite from your terminal, your browser should open up displaying your application and in your terminal, you should see something like this:

Getting up and running with lite-server
Getting up and running with lite-server

Change something within your index.html file and you’ll discover that your browser refreshes automatically and reflects that change!

Configuring the TypeScript Language

You can decide to use TypeScript or use vanilla JavaScript. Personally, I prefer TypeScript because it is JavaScript with some sugar added to it like type checking. So let’s go ahead and install TypeScript like so:

npm install --save-dev typescript

Create a file, tsconfig.json in your directory. All our TypeScript configuration live here.

Open up your tsconfig.json and add the following:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  }
}

Thanks to the "target": "es5" option, TypeScript will transpile our ES6 code to ES5 so that all browsers can understand the JavaScript code we write. Then we also want sourcemaps and decorators. In Angular 2, lots of decorators are used.

Go ahead and install these scoped npm packages like so:

npm install @types/node @types/core-js @types/jasmine --save-dev

The reason for installing these packages is to add type definitions to our project. It will make our editor support type-hinting, language highlighting for TypeScript, JavaScript and the modules from node we use in our code.

Open up your package.json file again, let’s add new commands in the scripts section like so:

"scripts": {
  "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\"",
  "test": "echo \"Error: no test specified\" && exit 1",
  "lite": "lite-server",
  "tsc": "tsc",
  "tsc:w": "tsc -w"
},

So, tsc is to start up TypeScript compiler & tsc:w is to watch for file changes. We also added the start command which will run three commands together concurrently. However, you can take advantage of Angular’s AOT compiler. It replaces the usage of tsc with ngc which is Angular template compiler (a drop-in replacement for tsc).

Wait a minute! How do we run three commands concurrently? Aha! Concurrently package to the rescue.

Setting up Concurrently

Concurrently is a nodejs package that allows us to run multiple commands concurrently. Let’s pull in the package.

npm install concurrently --save-dev

Now, run npm start from the terminal to start your application with lite-server and typescript working concurrently like so:

Running the lite server once again.
Running the lite server once again.

Your App is now being served locally.

Lite server is running
Lite server is running

Install Angular 2 Dependencies & Packages

With great power, comes great responsibilities. Angular 2 depends on some libraries & tools to wield such power.

  • zone.js simply makes our debugging much productive and supports change detection in our code
  • core-js standard library for JavaScript that includes polyfills for ES5, ES6, ES7 features in browsers
  • rxjs hands us observables and asynchronous data streams

So let’s go ahead and install these tools like so:

npm install zone.js core-js rxjs@5.0.3 systemjs --save

Having installed these dependencies, let’s pull in some packages that we will need to set up a basic Angular 2 app.

npm install --save \
  @angular/platform-browser \
  @angular/platform-browser-dynamic \
  @angular/core \
  @angular/common \
  @angular/compiler \
  @angular/http \
  @angular/forms \
  @angular/router

Using the SystemJS package loader

We need a loader to help load all the angular packages that we use in the app. Angular needs a tool to point it to where each and every package is whenever it invokes the package functionalities. SystemJS is a universal dynamic loader. It loads ES6 modules, CommonJS, AMD and global scripts in the browser and NodeJS.

Create a new file, systemjs.config.js, the SystemJS configuration file in the root directory and add the contents of this gist to it. Take a look at the gist first, too. You’ll see something like:

...

map: {
  // our app is within the app folder
  app: 'dist',

  // angular bundles
  '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
  '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
  '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
  '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
  '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
  '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
  '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
  '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',

  // other libraries
  'rxjs':                      'npm:rxjs',
  'ng2-file-upload':           'npm:ng2-file-upload',
  'cloudinary-core':           'npm:cloudinary-core',
  '@cloudinary/angular':       'npm:@cloudinary/angular',
  'Angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},

...

It looks for the application files to run in the app directory, you can change it to whatever directory you want. You can also see that it has specified what directory to look for angular packages, node_modules/@angular.

Take another good look at this section too:

...

// `packages` tells the System loader how to load when no filename and/or no extension
packages: {
  app: {
    main: './main.js', defaultExtension: 'js'
  },
  rxjs: {
    defaultExtension: 'js'
  },
  'ng2-file-upload': {
    main: 'ng2-file-upload.js', defaultExtension: 'js'
  },
  'cloudinary-core': {
    main: 'cloudinary-core-shrinkwrap.js', defaultExtension: 'js'
  },
  '@cloudinary/angular': {
    main: 'index.js', defaultExtension: 'js'
  }
}

...

This config lets SystemJS know how and what to load in the app.

Now head over to your index.html and reference the polyfills we installed earlier. Then load SystemJS like so:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Angular 2 app</title>

  <script src="node_modules/core-js/client/shim.min.js"></script>
  <script src="node_modules/zone.js/dist/zone.js"></script>

  <!-- Load our angular app with Systemjs -->
  <script src="node_modules/systemjs/dist/system.src.js"></script>
  <script src="systemjs.config.js"></script>
  <script>
    System.import('app').catch(function(err) { console.error(err); });
  </script>
</head>
<body>
  <h2>Setting up my development environment</h2>
</body>
</html>

Use Webpack as an Alternative

Webpack is a fantastic alternative to SystemJS. It is a popular module loader and bundler that helps load code from a server into a browser and generates static assets.

There are different ways of configuring Webpack for use in an Angular 2 application. One such way is installing webpack and its dev-server.

Note: Webpack 1.x was used here.

npm install webpack webpack-dev-server --save-dev

Then, you can install webpack loaders. Loaders help preprocess different types of files, so there are loaders for different files.

npm install --save-dev \
  angular2-template-loader \
  awesome-typescript-loader \
  CSS-loader \
  file-loader \
  html-loader \
  null-loader \
  raw-loader \
  style-loader \
  to-string-loader

Another advantage of using Webpack is the presence of plugins. Webpack plugins alter the behaviour of webpack for different scenarios. For example:

  • Defineplugin is used to define environment variables that we can reference within our app
  • UglifyJSPlugin minifies the bundles
  • NoErrorsPlugin stops the build if there is any error
  • ExtractTextPlugin extracts embedded css as external files

So, let’s install some plugins like so:

npm install --save-dev \
  html-webpack-plugin \
  webpack-merge \
  extract-text-webpack-plugin

Let’s configure Webpack. We will need a bundle for our application code, another for the vendor(angular libraries that were imported) code and a third one for the polyfills. So create a file, vendor.ts in the root directory.

import '@angular/platform-browser';
import '@angular/platform-browser-dynamic';
import '@angular/core';
import '@angular/common';
import '@angular/http';
import '@angular/router';
import '@angular/forms';
import 'rxjs';

Create another file, polyfills.ts, in the root directory like so:

import 'core-js/es6';
import 'core-js/es7/reflect';
require('zone.js/dist/zone');

if (process.env.ENV === 'production') {
  // production
} else {
  // development
  Error.stackTraceLimit = Infinity;
  require('zone.js/dist/long-stack-trace-zone');
}

Create a new file, webpack.config.js, the Webpack configuration file in the root directory like so:

module.exports = require('./config/webpack.dev.js');

Another advantage to using Webpack is the ability to have separate configuration for testing, development and production. So go ahead and create a config folder. The webpack config will reside here.

Create a file, webpack.common.js inside the config directory and add this code to it:

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const rootDir = path.resolve(__dirname, '..');

module.exports = {
  entry: {
    polyfills: './src/polyfills.ts',
    vendor: './src/vendor.ts',
    app: './src/main.ts'
  },
  resolve: {
    extensions: ['', '.js', '.ts']
  },
  module: {
    loaders: [{
      test: /\.ts$/,
      loaders: ['awesome-typescript-loader', 'angular2-template-loader']
    }, {
      test: /\.html$/,
      loader: 'html'
    }, {
      test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
      loader: 'file?name=assets/[name].[hash].[ext]'
    }, {
      test: /\.css$/,
      loaders: ['to-string-loader', 'css-loader']
    }]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ['app', 'vendor', 'polyfills']
    }),
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
};

Create a webpack.dev.js file inside the config folder too, like so:

const webpackMerge = require('webpack-merge');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const commonConfig = require('./webpack.common.js');
const path = require('path');
const rootDir = path.resolve(__dirname, '..');

module.exports = webpackMerge(commonConfig, {
  devtool: 'cheap-module-eval-source-map',
  output: {
    path: path.resolve(rootDir, 'dist'),
    publicPath: 'http://localhost:8080/',
    filename: '[name].js',
    chunkFilename: '[id].chunk.js'
  },
  plugins: [
    new ExtractTextPlugin('[name].css')
  ],
  devServer: {
    historyApiFallback: true,
    stats: 'minimal'
  }
});

In the code above, you can see that webpack.dev.js imports the webpack.common.js and defines some extra configuration like setting a directory for the build output, defining how the source map is created and removing the compiled css from the bundle to place it in a separate file.

The advantage of using SystemJS is that the setup is not as complex and verbose as Webpack. It’s simple and straightforward but Webpack offers a whole lot more functionalities and is needed as your app grows to become complex.

Build A Sample App With Cloudinary

Cloudinary provides an API for uploading images and any other kind of files to the cloud. These files are safely stored in the cloud with secure backups and revision history.

Cloudinary already takes away the pain of having to write large amounts of code to interact with their API by providing a new open source Angular 2 SDK that ships with simple, easy-to-use helper methods for:

  • Image uploading
  • Image administration and sprite generation
  • Embedding of images
  • Image transformation and manipulation

With the Angular 2 SDK, you can also adapt images for delivery on any device. Uploaded images can be manipulated, on-the-fly to deliver each user a version that suits the requirements of the viewing device while optimizing performance. Cloudinary can automatically crop images to focus on the most important region, select the most optimal quality and format and responsively deliver the image on any device in any resolution or pixel density.

What are we waiting for? Let’s get started on how to upload images in an Angular 2 app using Cloudinary!

1. Sign up for a Cloudinary Account

Signing up for Cloudinary is free.
Signing up for Cloudinary is free.

2. Enable “unsigned uploads” in the “Upload Settings” of your Cloudinary console

You’ll need to grab your cloud_name from the console.

3. We’ll just build on the already existing development environment we have that utilizes SystemJS, and use a few new packages.

  • @cloudinary/angular is the Cloudinary Angular 2 SDK and it depends on the cloudinary JavaScript library to function.
  • cloudinary-core is the core Cloudinary JavaScript library .
  • ng2-file-upload is an Angular 2 package that allows us to upload files.

So go ahead and install the cloudinary package and file upload packages like so:

npm install --save \
  @cloudinary/angular \
  cloudinary-core \
  ng2-file-upload

Once you are done, update your systemjs.config.js to the contents of this gist file. After that, open up your tsconfig.json file and add this:

...

"outDir" : "dist"

This is to ensure that all the .js files and sourcemaps that are produced from the TypeScript files are all in one directory rather than occupy space in the other directories.

4. Create the app.component.ts, app.module.ts, app.routing.ts, app.component.css, app.component.html, config.ts, main.ts and populate them.

In the app.module.ts file, we imported the Cloudinary modules like so:

// Cloudinary module
import {
  CloudinaryModule,
  CloudinaryConfiguration,
  provideCloudinary
} from '@cloudinary/angular';

Now add your Cloudinary details in config.ts like so:

export default {
  cloud_name: 'xxxxxxx',
  upload_preset: 'xxxxxx'
};

5. We’ll deal with photo uploading, listing the photos and perform some transformations on them with the aid of Cloudinary. So go ahead and create two folders, photo-list and photo-upload inside the app directory.

In the photo-list directory, add photo-list.component.css, photo-list.component.html and photo-list.component.ts.

In the photo-upload directory, add photo-upload.component.html and photo-upload.component.ts.

Also, don’t forget to create the models for your photos! Create a models folder inside the app directory and add photo.ts and photo-album.service.ts.

Note: Add <base href="/"> to your index.html file.

So Cloudinary provides us with some ready-made Angular 2 directives like:

  • <cl-image> allows you to easily fetch and display image on your web page from an external service like facebook
  • <cl-transformation> allows you to add all sorts of effects & transform the image. For a complete list of image manipulation options see the image transformations reference
  • <cl-video> allows you to embed a video element on your web page

A typical example is:

<cl-image public-id="{some_public_id}" class="thumbnail inline" angle="20" format="jpg">
  <cl-transformation height="150" width="150" crop="fill" gravity="north" effect="sepia" radius="20"/>
</cl-image>

6. Try running your app. You should be able to upload, list your images and perform some transformations on them, as seen below.

Cloudinary is alive!
Cloudinary is alive!

The complete source code for the application with SystemJS can be found on GitHub. The source code for this application with Webpack can be found here.

This application has also been integrated with Ahead of Time Compilation (AOT) and Rollup too. Check out the source code for that.

Now, if you need a zero-setup sample, feel free to checkout this plunker sample code that demos the use Cloudinary directives such as <cl-image>.

Conclusion! ⚡

We have looked at different ways of setting up a development environment for Angular 2 and also taken one of the easiest and very efficient approach of managing file uploads in your Angular 2 application. With Cloudinary, file management (images, videos, etc.) hassles should be a thing of the past!

For more detail on all the available Cloudinary Angular directives and components, check out the SDK documentation.

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (6)

Atti wrote

Thank you for this great article, really helps with the initial setup of angular 2.

I would really like some insight on the main differences between SystemJs and Webpack, pros and cons in the context of angular 2, maybe an idea for a future post :).

Btw, you can use npm init -y to automatically create a package.json file with the defaults without the step by step prompt.

Oliver Mensah wrote

Thanks for the great tutorial and it is well explained for even a layman to understand. Please my request from you is to write a book or make online video tutorial to cover most of the topics for us or me personally.

Good work.

RJ wrote

Angular-cli is available as a @angular/cli now. So you can install via npm install --global @angular/cli

Ross Lehr wrote

This is great and it works fantastic until I get to the part about integrating Cloudinary (which is a bonus tool I learned about here). Im not getting anything other than the initial html. the only console error I am getting is, “http://localhost:3000/dist/config.jsFailed to load resource: the server responded with a status of 404 (Not Found)” - there is not a “config.js” in the dist folder, nor do I see one in this tutorial (I know I may be missing something.

Any ideas? I can supply any other information you may need to help me fix this.

Thanks, Ross