Fast, faster, Angular CLI - how I converted my Angular project to use Angular CLI

Last week I migrated the codingmarks project to use Angular CLI and it’s awesome. Initially it was based on Angular Seed with Webpack - preboot/angular-webpack. I couldn’t take the update breaking changes pain anymore when I wanted to add Ahead-Of-Time compilation1 support. My webpack2 configuration was also getting out of hand. But I did it, I moved it to Angular CLI and in this post I am going to list the major steps I had to take. It is based on the Moving your project to Angular CLI story with my own needs and flavor added to it.

Conversion setup

The easiest way to move an existing project to Angular CLI is to copy your application files into a new, empty CLI project.

I started with preparing my existing project folder - bookmarks.

  • commit and push existing changes.
  • clean the folder from temporary files and ignored files using git clean -fdx.
  • renamed the project folder to old-bookmarks.

Before you run git clean -fdx, perform a git clean -fndx “dry run” (-n). This will show you which files are going to be removed without actually doing it.

Second step is to make a new project on the same parent folder as old-bookmarks using Angular CLI.

  • Verifiy the Angular CLI prerequisites.
  • Install the CLI globally: npm install -g @angular/cli.
  • Make a new app: ng new bookmarks --style=scss.
  • Move into the folder: cd bookmarks.
  • Test the app is working: ng serve --open.

I am using the Sassy CSS (SCSS)3 for the project. You can advise Angular CLI to generate a project with this style by specifying the --style=scss option.

Copy over your app files.

  • Remove the existing app: rm -rf src/app src/styles.css src/index.html e2e.
  • Copy src/app/, src/index.html from the old app. Move the app styles from src/app/app.scss to src/styles.scss
  • Copy over any other files hte app needs like images into src/assets. Adjust paths on the app to use this folder e.g. <img src='assets/my-image.jpg>.

webpack.config.js

Gone. Under the hood the whole thing still works via Webpack, but the configuration is done now via the .angular-cli.json file. I find this more readable and decently explained - Angular CLI Config Schema.

package.json

Compare the old-bookmarks/package.json to the new ./package.json. Add in third party libraries, the @types/* packages, and any other types. In my case you can see clearly what happened in this commit

Run npm install to install the added packages.

main.ts

For me it meant replacing the condition to enableProdMode() from if (process.env.ENV === 'build') to if (environment.production). See this commit for a complete comparison.

Unit Testing

No extra work done. Works automatically.

Commit changes

The final step is to copy your git history so you can continue working without losing anything:

Copy over the git folder: cp -r ../old-bookmarks/.git .git Commit and push the changes as normal.

Additional steps

There are stille some things I had to reconfigure, like making bootstrap, font awesome and showdown work as before.

Migrate bootstrap4

As I mentioned before I am using SASS and version 4 alpha of Boostrap

First thing - create an empty file _variables.scss in src/. Then, because I am using bootstrap-sass, I added the following to _variables.scss:

$icon-font-path: '../node_modules/bootstrap-sass/assets/fonts/bootstrap/';

The ~ syntax resolves the path like a module, basically replaces ../node_modules/ here

Finally in styles.scss add the following:

@import 'variables';
@import '~bootstrap/scss/bootstrap';

Using Font Awesome5

Font Awesome gives you scalable vector icons that can instantly be customized — size, color, drop shadow, and anything that can be done with the power of CSS.

To use it, I had to initially install it via npm install --save font-awesome. Similar as before, add then in the _variables.scss the following:

$fa-font-path: "~font-awesome/fonts";

Same in styles.scss:

//import font-awesome
@import '~font-awesome/scss/font-awesome.scss';

Using third party libraries6

I use showdown to enable markdown7 in the description field of bookmarks. To enable it, npm install showdown and @types/showdown. Then in code just import it and it’s ready to go:

import {Injectable} from '@angular/core';

import * as showdown from 'showdown';

// const showdown = require('showdown');
const converter = new showdown.Converter();

@Injectable()
export class MarkdownService {
  // converter object is not typescript

  toHtml(text: string) {
    return converter.makeHtml(text);
  }
}

This approach is valid for any other libraries that have available typings. Even without them you can still manually add them6.

Check out my post How to use Showdown in Angular and NodeJS to see how I used it before…

Environment specific configuration

Every application that runs on more than just localhost has environment specific configurations, that we need to address.

Before

Before moving to Angular CLI, I set the environment specific variables via the Webpack DefinePlugin8 and process.env9. The values would get replaced when building.

In webpack.config.js:

var API_URL = process.env.API_URL = '';
var isProd = ENV === 'build';
if (isProd) {
  API_URL = 'https://www.codingmarks.org/api';
} else {
  API_URL = 'http://localhost:3000/api';
}
...
new webpack.DefinePlugin({
  // Environment helpers
  'process.env': {
    ENV: JSON.stringify(ENV),
    'API_URL' : JSON.stringify(API_URL),
  }
})

and in code, where I would need the value:

constructor(private http: Http) {
  this.bookmarksUrl = process.env.API_URL + '/bookmarks/';
}

After

Now, with Angular CLI, there is the concept of different environments file like environment.ts for development and environment.prod.ts for production. These two files are created when a new app is generated via ng new app. So the API_URL is now set in the two files.

environment.ts (dev)

// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
  production: false,
  API_URL: 'http://localhost:3000/api'
};

and

environment.prod.ts (prod):

export const environment = {
  production: true,
  'https://www.codingmarks.org/api'
};

The magic happens during builds - see in comments above

Then, in code just import the environment to get access to the desired value:

import { environment } from 'environments/environment';

@Injectable()
export class BookmarkService {

  private bookmarksUrl = '';  // URL to web api

  constructor(private http: Http) {
    this.bookmarksUrl = environment.API_URL + '/bookmarks/';
  }

Speed

I saved the best for last - speed of development, speed of building and speed at runtime ( not that in my case, the website was not loading rapidly with Just-In-Time Compilation (~500ms), but Ahead-of-Time Compilation (~280ms) takes it one step further ).

With this I rest my case.

PS: By the way you can find most of links referenced here if you filter the public bookmarks for the **angular-cli tag** in codingmarks - http://codingmarks.org/search?q=[angular-cli]

References

Adrian Matei

Adrian Matei
Life force expressing itself as a coding capable human being

How to redirect domain to www url with nginx

Snippet from nginx config file that redirects all requests (http and https) to the www URL Continue reading