Update 07/19/16: The angular-cli project structure and config files have changed slightly since this writing. As of the moment, it should affect the content below, but it may have a small impact in the future.

Frontend Development with Angular 2

This post is the fourth in a series that explains how data scientists can turn their scripts into consumer-facing web applications. The intro post for this series explains the scope of the project and contains an index of other posts.

The code seen in this post can be found in the frontend-layout branch of the GitHub repo.

cd /to/desired/directory
git clone -b frontend-layout https:[email protected]/coshx/portfolio_optimizer.git
cd portfolio_optimizer

Problem, Goal, Solution

  • Problem: Users lack a web interface where they can submit requests to our data service and view the results.
  • Goal: Create the guts of a frontend application so that users will be able to visualize data from our data service.
  • Solution: Use angular-cli to set up Angular 2 and Typescript. Then hook up a bootstrap template and a few UI elements.

By the end of this post, our application will have the navbar and the three panels seen in the mockup below.

Setting Up Angular 2 and Typescript

As of this writing, Angular 2 is in the beta “Release Candidate” phase (v2.0.0-rc.1). Since changes to Angular 2.0 are a guarantee, I recommend using angular-cli, the command line interface built by the Angular team, to seed your project. Here are my arguments for using angular-cli over one of the various starter packs on GitHub:

  1. working unit and end-to-end tests out of the box
  2. the ability to generate new files with the necessary boilerplate code from the command line
  3. convention over configuration
  4. support from the Angular development team

The angular-cli homepage and GitHub provide a rundown of the command line interface’s capabilities. It is based on the ember-cli, so expect some of the same functionality if you’re familiar with Ember.

Node and npm

If you are new to the frontend JavaScript world, you will need to install Node, which is a JavaScript runtime environment that uses Google’s V8 JavaScript engine to execute JavaScript code. You will also need npm, which is a package manager for Node. If you are familiar with Python and pip, Node resembles the Python interpreter, and npm resembles pip. The video instructions at the npm site should be enough to get you up and running with Node and npm. Node.js is also available on homebrew using brew install node.

angular-cli

Next, we need to use npm to install angular-cli and its dependencies globally. Run npm install -g angular-cli, and npm should do the rest. Typescript is a dependency of angular-cli, so it will be installed with all the other dependencies. Now, we can create a new project from the command line and see what it looks like. (Note: angular-cli currently takes a long time to build a new project. Don’t be alarmed.)

cd /to/favorite/directory
ng new stocks
cd stocks
ng serve

If you navigate to http://localhost:4200, there should be a “Loading…” prompt, followed by a notice that “stocks works!”. If you are interested, you can run unit tests using ng test and end-to-end tests using ng e2e. Note that end-to-end tests require an active ng serve process in another terminal window.

Typescript

There are a number of primers on TypeScript, including Learn X in Y Minutes, DefinitelyTyped, and of course, the official documentation. Before diving in too far, be sure to configure your favorite text editor to work with Typescript, as detailed on the TypeScript home page. The big takeaways about Typescript are:

  • Typescript is a superset of JavaScript, meaning that all valid JavaScript programs are also valid TypeScript programs.
  • Typescript files (.ts) must be transpiled to JavaScript (.js). Every time we build a project, our TypeScript files (mostly found in the src folder) are transpiled and placed in the dist folder. More info on TypeScript transpilation can be found here.
  • Typescript adds static typing and class-based object-oriented programming to JavaScript. This means we get compile-time checks on object types and a bevy of new keywords and useful syntax, like modules, classes, interfaces, and the beloved “arrow” syntax for anonymous functions.

Angular 2

Why use Angular 2 as the frontend of our portfolio optimizer? Some of the biggest benefits include:

  • Modularity: Angular 2 and TypeScript encourage highly modular code, which keeps code bases small and enables extensive code reuse. For example, reusing data visualization components speeds up development.
  • Reactive: Angular 2’s use of rxjs allows for better handling of asynchronous processes.
  • Templating syntax: Views are easy to create and manage, removing the need for extensive jQuery.
Angular 2 Project Structure

A bit of orientation is always nice when dealing with a new framework. Once inside the stocks directory, you should see a file structure like this:

.
├── angular-cli-build.js
├── angular-cli.json
├── config
├── dist
├── e2e
├── node_modules
├── package.json
├── public
├── src
├── tmp
├── tslint.json
├── typings
└── typings.json

These files and directories fit into a few categories:

  • Angular Configuration:
    • angular-cli-build.js: Specifies the library modules that our Angular 2 app will be able to access.
    • angular-cli.json: Contains project-specific configuration, such as the test runners to use and location of project files.
    • config: Directory containing configuration files for testing and development vs. production.
  • Node Configuration
    • node_modules: Directory containing the packages in package.json.
    • package.json: Specifies the npm package dependencies for our application.
  • Typescript Configuration
    • typings: Directory containing TypeScript definitions.
    • typings.json: Specifies a list of the required TypeScript definitions.
    • tslint.json: Provides rules to the TypeScript linter.
  • Project Files
    • dist: After passing through the TypeScript compiler, JavaScript is served from this directory for local development and testing.
    • e2e: Contains end-to-end tests, which run using karma.
    • public: The built, ready for deployment application is dropped here.
    • src: Contains project files and some further TypeScript configuration.
What Happens on ng serve?

In short, angular-cli takes care of transpiling TypeScript into JavaScript and serving this up on a development server when you enter ng serve. Here’s a bit more detail:

  1. The browser loads index.html: src/index.html is the browser’s entry point to the application. A <script> tag points the browser to our project’s dependencies, which are served from the dist/vendor directory. It also calls system-config.js and main.js (see 2 and 3 below).
  2. ES6 modules are loaded: Every module and library that our application depends on is specified in src/system-config.ts. SystemJS will load these ES6 modules for us.
  3. Application Bootstrapping: SystemJS loads src/main.ts, which “bootstraps” our application. The bootstrap() call in src/main.ts creates an instance of our application and provides it with any ES6 dependencies that should be available application-wide.
Add a Bootstrap Template

Our application is pretty bare, so let’s spruce it up with a Bootstrap template. Currently, the most reliable way of doing this is loading Bootstrap from a CDN, which comes at the cost of increased load time. Although a few projects are developing bootstrap directives for Angular 2, these are liable to change in the coming months. Meanwhile, Bootswatch is a good source of well-maintained templates. Find a template that you like, click the dropdown tooltip for it. Then, copy the <link href> code, and add this code under <head> in /src/index.html:

<!doctype html>
<html>
  <head>
    ...
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.6/flatly/bootstrap.min.css" rel="stylesheet" integrity="YOUR-TOKEN-HERE" crossorigin="anonymous">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    ...
  </head>
</html>

Now when you run ng serve, “stocks works!” should be in a newly styled font.

Add a Navigation Bar

Bootswatch’s site has a guide to UI elements for each bootstrap template. To add a navbar, click on your template of choice, scroll down to the navbar section, and click the “< >” that appears when you hover over one of the navbars. This will return the corresponding HTML.

Copy and paste this generated HTML at the top of /src/app/stocks.component.html.

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">Brand</a>
    </div>

    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
        <li><a href="#">Link</a></li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Dropdown <span class="caret"></span></a>
          <ul class="dropdown-menu" role="menu">
            <li><a href="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li class="divider"></li>
            <li><a href="#">Separated link</a></li>
            <li class="divider"></li>
            <li><a href="#">One more separated link</a></li>
          </ul>
        </li>
      </ul>
      <form class="navbar-form navbar-left" role="search">
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Search">
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
      </form>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#">Link</a></li>
      </ul>
    </div>
  </div>
</nav>
<h1>
  {{title}}
</h1>

Now, when you run ng serve and go to your development server, you should see a navbar. Of course, this navbar doesn’t work yet, but things are starting to look better!

Add Panels

The last order of business is to get the three panels from our mockup into the application. This is our first major architectural design decision. I will put off a full explanation until the next post and instead humbly submit that we want to create an architecture like the following:

This architecture places our portfolio optimizer in its own OptimizerComponent, allowing us to add more sibling “pages” (like volatility.component.ts) to our site in the future. The OptimizerComponent has three child components, which will house each of the panels in our mockup: InputComponent, BarchartComponent, and ResultsTableComponent. We can construct this architecture using angular-cli by creating the OptimizerComponent from within the /app directory, then moving into the /optimizer directory before creating the three components for the panels.

cd /stocks/src/app
ng g component optimizer
cd optimizer
ng g component input
ng g component barchart
ng g component results-table

When we created our four new components, angular-cli gave us a special template selector for each component. This can be found in each component’s .ts file. This selector is an example of an Angular Directive, and we can use it like an HTML tag in templates. I renamed my selectors to get rid of the annoying “app-” that angular-cli prepends.

app-optimizer      -> optimizer
app-input          -> user-input
app-barchart       -> barchart
app-results-table  -> results-table

Getting our panels in place will be similar to adding our navigation bar. The only added complication is that our new components (and their templates) do not know about one another. Here are the steps involved:

  1. Add the HTML that will create our panels.
  2. Import child components into their parent components (i.e. from StocksAppComponent import OptimizerComponent and from OptimizerComponent import the three panel components).
  3. Reference child components within their parent component’s HTML template.

From Bootswatch, we can grab code for a panel underneath the “Containers” heading. I added variations on the following code to the templates of OptimizerComponent‘s children.

<div class="panel panel-default">
  <div class="panel-heading">Add Stocks</div>
  <div class="panel-body">
    <p>
      input works!
    </p>
  </div>
</div>

Next we need to reference each of these templates in the OptimizerComponent template.

<div class="row">
  <div class="col-md-4">
    <user-input></user-input>
  </div>
  <div class="col-md-8">
    <barchart></barchart>
  </div>
</div>
<div class="row">
  <div class="col-md-12">
    <results-table></results-table>
  </div>
</div>

Next, to make the OptimizerComponent aware of its children and their special HTML tags, we need to import them into optimizer.component.ts and add them to the list of directives that OptimizerComponent uses.

import { Component, OnInit } from [email protected]/core';

import { InputComponent } from './input/input.component';
import { BarchartComponent } from './barchart/barchart.component';
import { ResultsTableComponent } from './results-table/results-table.component';

@Component({
  moduleId: module.id,
  selector: 'optimizer',
  templateUrl: 'optimizer.component.html',
  styleUrls: ['optimizer.component.css'],
  directives: [InputComponent, BarchartComponent, ResultsTableComponent] // listing directive metadata
})
export class OptimizerComponent implements OnInit {

  constructor() {}

  ngOnInit() {
  }

}

At this point, OptimizerComponent should be aware of its children because it has import statements in the component and it has references to its children in its HTML. However, we won’t see anything new yet in our application because our StocksAppcomponent does not know about its child, OptimizerComponent. Let’s rig that up now. In stocks.component.html, replace the <h1> tags at the end of the template with:

<div class="container" style="margin-top: 100px;">
  <optimizer></optimizer>
</div>

Then in stocks.component.ts, add an import statement and directive metadata for OptimizerComponent:

import { Component } from [email protected]/core';

import { OptimizerComponent } from './optimizer/optimizer.component';

@Component({
  moduleId: module.id,
  selector: 'stocks-app',
  templateUrl: 'stocks.component.html',
  styleUrls: ['stocks.component.css'],
  directives: [OptimizerComponent]      // directive metadata
})
export class StocksAppComponent {
  title = 'stocks works!';
}

Now when you run ng server, you should see our three panels floating around nicely. Next time we’ll set up the router for our application and talk more about Angular 2 architecture.