Monday, August 8, 2016

Learning Angular 2: Creating My First Sandbox Web Application

While there is still a lot out there for me to read regarding Angular 2, I tend to learn by coding and solving problems.  Even though there are a few aspects of Angular 2 that are in flux at this time (like forms), I feel that I can start writing an application without much fear that I'd have to go back and redo things because the API has changed.

So I've created my first "sandbox" Angular 2 application where I can practice writing Angular code and figure out ways to accomplish specific application tasks with Angular 2.  I'm going to keep a copy of the code up on GitHub and release milestones in my development so that I have a historical picture of the development and so I can potentially backtrack and create different solutions to a given problem.  Plus, it will allow anyone to pull down a tagged version on their own machine to look at the code.

My first sandbox is an application called "GuildRunner".  My plan is that it will be an application for managing a fictional collection of trade guilds, and so I can use it to exploring dealing with common application issues like authentication and authorization, data relationships, and searching.  But I wanted to start by simply creating the foundation for the application structure and getting it up and running.

So I started by creating the repo in GitHub, and then cloned the repo into a new IntelliJ IDEA project via the IntelliJ option for creating projects via GitHub.  I then opened a command prompt in the project directory and invoked the Angular CLI command "ng init" to create the starting project files: I was pleased that the command let me decided whether or not to overwrite the README.md file cloned from GitHub.  Another nice thing about the CLI-generated file I hadn't noticed before:  the .gitignore file is configured to ignore the IntelliJ-specific files as well as the node_modules and typings folder during commits, which is nice.

At that point, I had a basic, single-component app that I could run with the "ng serve" command of Angular CLI.  But I wanted to use the in-memory web API (at least initially) to provide the data for the application as if it was interacting with a server and a database, so I needed to reconfigure the application to utilize that feature.  The details of that reconfiguration ended up as a separate blog post: Adding the In-Memory Web API to a SystemJS-based Angular CLI Application.

In the Tour of Heroes example of using the in-memory web API, the mock data was defined/written out within the InMemoryDataService createDB() method.  Since I plan on creating a fair amount of mock data, I created a "db" folder under the "app" folder that would house all the modules that would export the data collections.  I then created my first bit of mock data: a single version record.


//src/app/db/version.ts

let version = [
  {id: 1, name: '0.0.1'}
];

export { version }

...and provided that to the InMemoryDataService via an import:


//src/in-memory-data.service.ts

import { version } from './db/version';

export class InMemoryDataService {
  createDb() {
    return { version };
  }
}

The purpose of the version record was to have some data to display on the main application page that would confirm that the in-memory web API was working (and also confirm the version of the sandbox application I was working with). So with the Tour of Heroes code as a guide, I created a VersionService to retrieve the version data and a VersionComponent to display it:


//src/app/version/version.service.ts

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

import 'rxjs/add/operator/toPromise';

@Injectable()
export class VersionService {

  private versionUrl = 'app/version'

  constructor( private http: Http ) { }

  getVersion() {
    return this.http.get(this.versionUrl)
      .toPromise()
      .then(response => response.json().data )
      .catch(this.handleError);
  }

  private handleError(error: any) {
    console.error('An error occurred', error);
    return Promise.reject(error.message || error);
  }
}

//src/app/version/version.component.ts

import { Component, OnInit } from '@angular/core';
import { VersionService } from './version.service';

@Component({
  moduleId: module.id,
  selector: 'app-version',
  templateUrl: 'version.component.html',
  providers: [
    VersionService
  ]
})
export class VersionComponent implements OnInit {

  versionNumber = '-.*.-';

  constructor( private versionService: VersionService ) { }

  ngOnInit() {
    this.versionService.getVersion().then( versions => this.versionNumber = versions[0].name );
  }

}

<-- src/app/version/version.component.html -->

<p><strong>Version:</strong> {{versionNumber}}</p>

Then (after adding Bootstrap to the project and adding some Bootstrap layout containers), I added the VersionComponent as a sub-component of the AppComponent:


//src/app/app.component.ts

...
@Component({
  ...
  directives: [
    VersionComponent
  ]
})

<-- src/app/app.component.html -->
<div class="container">
  <div class="row">
    <div class="col-md-10">
      <h1>{{title}}</h1>
    </div>
    <div class="col-md-2 text-right">
      <app-version></app-version>
    </div>
  </div>
</div>

Once all of that was done, I could run my application using "ng serve" and a moment after the page loaded I could see the version number.

I finished up this version of the sandbox by updating the current set of unit and end-to-end (e2e) test files such that they would pass. Previous experience with e2e testing let me update the single e2e test pretty easily, but getting the minimalist unit tests to work took some trial-and-error, and I don't yet have a clear sense of how to set up the unit tests such that the component under test has all the dependencies (or mocks of the dependencies) it needs.

The release of the GuildRunner sandbox that contains the application foundation code and the changes described above can be found on the GuildRunner GitHub repo as release 0.0.1:

https://github.com/bcswartz/angular2-sandbox-guildrunner/releases/tag/0.0.1 

Instructions for running the sandbox on your own machine via Angular CLI can be found on the main page of the GitHub repo.

No comments:

Post a Comment