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.

Thursday, August 4, 2016

Adding the In-Memory Web API to a SystemJS-based Angular CLI Application

8/6/2016 EDIT: On 8/2/2016, the Angular CLI was updated to reflect the fact that the CLI was being refactored to use Webpack instead of SystemJS.  Currently, an npm install of Angular CLI will still give you a version that uses SystemJS, and the following instructions apply to that SystemJS version.

As of version 1.0.0-beta.10, the Angular CLI tool does not provide an option for generating a base Angular 2 application that includes the in-memory web API, which is a tool that lets developers simulate the return of data from HTTP calls.  I figured adding the in-memory web API to my CLI-generated application was just a matter of mimicking how the HTTP lesson in the Tour of Heroes tutorial did it, but it was a bit more involved than that. Here's how you do it.

(From here on out, I'm going to abbreviate "in-memory web API" as IMWA for the sake of brevity. Someone needs to come up with a shorter, cooler name for this tool.)

First, you add the IMWA as a dependency in the package.json file:


"dependencies": {
  ...
  "angular2-in-memory-web-api": "0.0.14"
}

...and then run "npm install" from the command prompt in the directory containing the package.json file to download the IMWA node module (I tend to delete my entire "node_modules" folder first just to make sure everything installs fresh).

Then you add the neccessary imports to your main.ts file and use those imports in the bootstrap() method:


import { XHRBackend } from '@angular/http';

import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api';
import { InMemoryDataService }               from './app/in-memory-data.service';

import { HTTP_PROVIDERS } from '@angular/http';

...

bootstrap(AppComponent, [
  HTTP_PROVIDERS,
  { provide: XHRBackend, useClass: InMemoryBackendService },
  { provide: SEED_DATA, useClass: InMemoryDataService }      
]);

Then create a in-memory-data.service.ts file in your src/app directory with some temporary placeholder data:


export class InMemoryDataService {
  createDb() {
    let tempData = [
      {id: 1, name: 'foobar'}
    ];

    return { tempData };
  }
}

Up to this point, all of the setup is nearly the same as it was for the Tour of Heroes tutorial, but the final changes needed are CLI-specific.

When you use the CLI's "ng serve" command to compile and run the application on your local machine, the CLI generates a "dist" directory with all of the necessary files to execute the application. The "dist" directory consists of:

  • The index.html and global configuration files (main.js and system-config.js) in the root of the "dist" folder.
  • The "app" folder which contains the rest of the Angular 2 files specific to your application, from the main component file on down.
  • A "vendor" directory that contains the files from the various node modules needed to run the application, such as the core Angular 2 library files.

The IMWA needs to be included as a separate folder under that "vendor" directory in order for the IMWA to be available to your application.

The "angular-cli-build.js" file in the root of your project filespace (in the same directory as the package.json) controls which node modules make it into the build (the "dist" folder). Add the IMWA to the array of modules like so:


vendorNpmFiles: [
  ...
  'angular2-in-memory-web-api/*.+(js)'
]

The "*.+(js)" syntax ensures that all of the ".js" files in the IMWA node module are copied to the appropriate folder ("angular2-in-memory-web-api") under "vendor" (at this time, there are no ".js.map" files in the IMWA module).

Finally, you need to make sure the IMWA module in the "vendor" directory is loaded by SystemJS along with the regular Angular modules by adding the IMWA to the package configuration in the "system-config.ts" file in the "src" directory of your project:


/** Map relative paths to URLs. */
const map: any = {
  'angular2-in-memory-web-api': 'vendor/angular2-in-memory-web-api'
};

/** User packages configuration. */
const packages: any = {
  'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};

That should do the trick: run your app using "ng serve", open up the browser console, and confirm that there are no 404 error messages regarding the IMWA.

Sunday, July 31, 2016

IntelliJ 2016.2 and Angular 2 Support

I realized today that the latest update (version 2016.2) for my IDE of choice - IntelliJ IDEA (Ultimate version) - includes additional support for Angular 2:

  • A collection of live templates for Angular 2 files such as components and services.
  • A better understanding of template syntax.
  • The ability to create a new IntelliJ project via the Angular CLI tool.
So I downloaded and installed the update, but that alone wasn't sufficient to access these new features.  Turns out I had never installed the "AngularJS" plugin (I remember coming across it before, just hadn't installed it).

Once I installed that plugin and restarted IntelliJ, I could select File -> New -> Project from the menu tree, and "AngularJS" (for Angular 1 projects) and "Angular CLI" were new options listed under the "Static Web" project option.  I went ahead and chose "Angular CLI", and IntelliJ invoked the global install of Angular CLI on my laptop and executed the "ng-init" command to create the application structure and foundation files for a new Angular 2 project.

Inside the Angular project, I could create a new component using live templates by creating a new empty TypeScript file, hitting Control/Command-J to insert a live template, typing "ng2-component" until it was the selected template and hitting the Tab key.  The template then lets you tab through the update points in the template so you can enter the directive, template, and component names you want.

Very cool, but I think I would probably end up creating my components using Angular CLI from within the Terminal window in the IDE, because the CLI can generate the full set of files for a given component (the TypeScript file, the template HTML file, the CSS file, and the unit test file).  It also looks like the templates that contain code related to routing need updating.

Still, it's always nice when your IDE adds new features to making your coding a little easier.

First Impressions of Angular CLI

Before creating a demo Angular 2 project of my own from scratch, I decided to play with Angular CLI, the command line tool provided by the Angular team to help streamline Angular 2 development.

There are a number of posts and articles out there about Angular CLI, so I'll only share a few personal observations:

  • The "--help" option for displaying documentation for the overall list of commands or individual commands is well executed:  much more useful and readable than most command line tool documentation.

  • I really like the "dry-run" option provided with the commands that generate the application skeleton and config files.  It lets you see a list of the files and folders that would otherwise be created by the command without actually creating them, giving you an idea of what to expect.

  • The application skeleton structure is a bit different from the structure used in the Quick Start and Tour of Heroes tutorial, moving the "app" directory under an "src" directory.  It does this to make room for a "dist" directory parallel to the "src" directory where it can output runtime files for testing and environment-specific distribution builds.  I found it interesting that it places the "main.ts" file in that "src" directory instead of the "app" directory.

  • I liked how the default development distribution build retains the separate .js and .js.map files for the project code files, while the production build concatenates those files and generally packages your assets to make them more efficient.

  • The "ng generate" command is most useful for generating new components.  The generated component contains the appropriate @angular/core imports and @Component() metadata, contains an empty constructor method, and implements ngOnInit.  Option flags used with the generation command can customize certain aspects of the generated component, such as whether the component uses external HTML and CSS files (the default) verses inline template and inline styles, and whether all of the component assets are bundled in a separate directory.  The other files you can generate with the command are mostly empty shells (although generated service files do include the @Injectable() decorator).

  • I like that the generated components use the module.id technique to handle relative pathing for the templateUrl and styleUrls properties.

  • The CLI documentation refers to the types of files you can generate with "ng generate" as "blueprints".  I hope that means that there will one day be an option to add your own personal blueprints into the mix.

  • It's interesting that "ng test" starts by creating a development build in the "dist" folder before performing the tests...presumably because the tests are run against that folder.  I'm sure there's a reason for doing it that way (making sure everything works after packaging?), but it makes the startup time for testing slow.  On the flip-side, once it's started it watches for file changes and re-tests on the fly, so if your coding process involves running unit tests in the background all the time the start-up time penalty is a one-time cost.  I also wonder what the implications are for performing unit testing via testing tools in your IDE:  would those IDE tools also need to test the build files rather than the .js files in the "app" folder?

Overall, I really like the tool so far, and I look forward to seeing it evolve alongside Angular 2.

Sunday, July 24, 2016

Recognizing TypeScript's Jurisdictional Boundary

While I was exploring the current Angular 2 documentation regarding forms and form field validity, I caught myself wondering why the Angular code wouldn't block or complain about an attempt to assign an incorrect data type value to an object property.  Given the following TypeScript object:


export class Villain {
  id: number;
  name: string;
  age: number;
  dateOfBirth: Date;
}

...you could be forgiven if you thought, for a brief moment at least, that a user entering property values for a Villain via a form would experience an error of some kind if they tried to enter a non-number in the age form field,or a string value of "7/14/84" in the date of birth field.

But of course that wouldn't happen. TypeScript only enforces those types when it compiles the code, preventing programmers from using the wrong data type in the code. That type enforcement is not carried through to the resulting JavaScript.

This is hardly a revelation.  TypeScript is a tool for writing JavaScript: it doesn't alter the base behavior or functionality of JavaScript.  But I can see developers spending a few hours coding classes and service methods in TypeScript, then turning their attention to the code that interacts with the web UI and having to remind themselves that the type protection doesn't apply to user/UI actions that change the class property values.  In that area of the code, you have to enforce the data types with explicit code.

And it made me wonder if there should be a way to carry those data type restrictions on class properties down to the resulting JavaScript code by default.  Not sure how feasible that would be.  I would think you'd have to make each property a private property with getter/setter methods where the setter method would ensure the incoming value met the data type criteria.  But then how would a data type mistmatch be handled?  You probably wouldn't want to throw an error:  you'd want to record the attempt in some readable property.  Would you prevent the property from being set to an invalid value, or would you allow it and count on the developer to write code to inspect the object for validity issues before proceeding?  And how would you provide a mechanism for adding custom validations on top of the data type validations?

No matter how you went about it, you'd end up with an opinionated process for enforcing the data types that probably wouldn't work for everyone, which is probably why TypeScript doesn't do anything like that with the compiled JavaScript code.

Learning Angular 2: Exploring the Current Features of Forms

One of the things I noticed when I completed the official "Tour of Heroes" Angular 2 tutorial was that there wasn't a lesson on using forms:  in Angular 1 input bindings that were managed under ngForm provided data state, validation, and error-handling features, and I had heard that Angular 2 had the same thing.

Apparently forms are another aspect of Angular 2 that is still somewhat of a moving target: the current "Forms" chapter under the "Basics" category of the Angular 2 site documents a deprecated version and points to a newer documentation page.  I decided to read through the newer documentation page and try out the current forms functionality myself, building off of my existing Tour of Heroes project codebase.

The decision to use my Tour of Heroes code ended up causing a few problems.  The first problem I ran into was that I didn't have an "@angular/forms" module to import the form providers from per the documentation instructions.  It's not included in the package.json file used for both Tour of Heroes and the QuickStart.  An exection of "npm view @angular2/forms" told me that the forms module current version was "0.2.0".  I updated package.json, deleted my current node_modules folder, and ran "npm install", and after that I had an "@angular/forms" module folder, and I thought I was in business.

However, I got a 404 error trying to load "@angular/forms" when I tried to run the application.  That one caused some head-scratching until I realized where I went wrong,  Having been several weeks since I set up the Quick Start tutorial and then later having copied over those configuration files, I had forgotten about the role of the systemjs.config.js file.  The array of ngPackageNames determines what packages under the "@angular" node_modules folder are loaded, and "forms" was not in the array.  Once I added it, the error went away and I could actually focus on the exercises in the documentation.

The biggest takeaway from the page was that using the [{ngModel}] binding on a form control leads to that form control being decorated with CSS classes that describe the state of the form control:

  • ng-untouched vs. ng-touched, which indicate if the user has interacted with the form control via the mouse or keyboard.
  • ng-pristine vs. ng-dirty, which indicate whether the value of the form control has changed.
  • ng-valid vs. ng-invalid, which indicates if the value of the form control is valid or invalid.
There are some nuances to those explanations, some of which were explained on the documentation page and some that I determined for myself:
  • The class change from untouched to touched doesn't take place until the form control loses focus after the user has touched it (put the form control in focused stated) with either the mouse of keyboard.

  • The class change from pristine to dirty only takes place if the user changes the value of the form control via the UI, such as by typing or by performing a paste action in the form control.  Changing the value of the model data bound to the input programmatically does not trigger the change from pristine to dirty.

  • The untouched-to-touched and pristine-to-dirty transitions are one-way transitions.  If you delete the last letter of the value in a text input and then restore it (so the value is the same as it was when the text input DOM element was created on the page), the text input is still labeled with ng-dirty.  The document emphasizes this point with an example of how to "reset" the pristine state of the form controls by destroying and recreating the form using ngIf and a conditional, and hints that a proper "form reset" action may be forthcoming.  There is a GitHub issue on the topic:  https://github.com/angular/angular/issues/4933.  Having had some programmatic experience with resetting form values, I'm interested to see how they solve the reset issue.

  • The documentation page demonstrates the transtion of a text input control from validity to invalidity in conjunction with the use of the "required" attribute on the <input> element. But it currently doesn't explain how Angular determines the validity of the form control value under other circumstances.  Angular didn't mark the input as invalid when I entered a non-URL value in an input with the HTML5-supported type of "URL", nor did it react when I entered a value in another text input that exceeded the value of the "min" attribute.  A search through the Angular.io site didn't turn up any page that clearly explained how to perform the validation with the latest implementation of forms, though the references to Validators and their use implies that the answer likely involves applying validation rules/logic programmatically.

The documentation page concluded with code exercises that demonstrated how the form as a whole has a validity state property (courtesy of the ngForm directive that is quietly attached to the <form> tag automatically) which is affected by the validity/invalidity of the individual form control values, and then how the form validity property can be used to block the submission of the form if it's currently invalid.

After going through this page, I can see why forms were not covered as a topic in the Tour of Heroes tutorial.  The implementation of form-related behavior in Angular 2 is still evolving, and though this documentation page illustrates some of the expected behavior and benefits it does so at a basic and somewhat vague level. I'll have to revisit this topic down the road after the documentation is more fleshed out.

Saturday, July 16, 2016

Learning Angular 2: Tour of Heroes Tutorial, Lesson 7

The final lesson of the Tour of Heroes tutorial covers using Angular 2 with HTTP.

It starts off with what seems to be a bit of a contradiction:  it shows how to add the set of HTTP services to the application via the bootstrap invocation in main.ts (following the pattern set to add the router), but then makes a point of mentioning that usually application-wide services are registered in the AppComponent providers (HeroService being the prime example in the tutorial up to this point).  The implication seems to be that if we didn't need to mock the HTTP transactions because we have no actual server to talk with, we could register HTTP_PROVIDERS in the AppComponent, but I wish they had said that explicitly.

The revisions to the HeroService to utilize HTTP calls start with a revision that includes adding "Http" to the new constructor method, but leaves out mentioning the need to import Http into the file.  Same with the Headers class used in the new service methods for updating heroes, so HeroService needs the following imports:


import { Headers, Http } from '@angular/http';

Interesting how the import of the rxjs toPromise() operator is not imported with a variable reference like the rest of the imports.

One thing that's not explained in the lesson is the relationship between the heroesUrl ( 'app/heroes') and the array of heroes in the in-memory-data.service.ts file. According to the in-memory web API documentation, the latter half of the "URL" in this particular call references a key name created in the createDB() method that refers to an array of objects. So changing the HeroService to call from a list of monsters instead of heroes is as easy as:


//in-memory-data.service.ts

createDb() {
  //…The heroes array in the lesson
  let monsters = [
    {id: 11, name: 'Mr. Munch Munch'},
    {id: 12, name: 'Grumpy Pants'},
  ]
  return { heroes, monsters };
}

//hero.service.ts

private heroesUrl = 'app/monsters';

And you can target an individual object in the data array by id value, as is done in the put() and delete() methods described in the lesson.  I wondered though if that meant you were locked into a convention of having an "id" property, so I looked around and found the GitHub page for the in-memory-web-api, which said you could specify the property name to use as the identifier as well as the value ("id" apparently being the default property name if none is specified).  I couldn't get the example syntax where the property name was preceded by a "?" (so maybe the documentation is a tad out-of-date), but I could create a URL targeting a Hero by name instead of id without the "?":


let url = `${this.heroesUrl}/name=${hero.name}`;

The need to add the Content-Type header to every add, update, and delete action to specify the use of JSON caught my eye.  It makes sense given the http methods take an array of headers as an argument, but I could see making a private function that would handle creating and returning that headers array that the http functions could all share. Like so:


private getHeaders(): Headers {
  let headers = new Headers();
  headers.append( 'Content-Type', 'application/json' );
  return headers;
}
//...
private put(hero: Hero) {
  let url = `${this.heroesUrl}/${hero.id}`;

  return this.http
    .put( url, JSON.stringify( hero ), { headers: this.getHeaders() } )
    .toPromise()
    .then( () => hero )
    .catch( this.handleError );
}

The next step of the lesson involves updating the code of the HeroDetailComponent to invoke the public save() method on the HeroService (which calls either the put() or post() private service methods as appropriate).  I don't know why the @Input decorator is applied to the hero property of HeroDetailComponent:  while it's true that later in the lesson HeroDetailComponent is once again made a subcomponent of the HeroComponent, the HeroComponent never passes a Hero object to HeroDetailComponent, and removing the @Input decorator doesn't break any of the new add, update, and delete functionality.

The @Output decorator and the "close" EventEmitter object are a different story.  At first, when I was simply following along with the lesson, I didn't pick up on exactly how the HeroComponent knew to listen for the emission of the saved Hero object that occurs in the HeroDetailComponent goBack() method.  The HeroComponent is coded to react to the "close" EventEmitter of HeroDetailComponent through the event handler put on the directive:


<my-hero-detail (close)="close($event)"></my-hero-detail>

I find it interesting that the argument passed to the close() method of HeroComponent is "$event", and yet what the goBack() method emits and what the close() method expects as its argument is a Hero object. If I change the argument name in the directive from $event to something else, like "sentHero", the incoming argument in the close() method ends up as undefined. In contrast, the delete event handler in HeroComponent passes both a Hero object and an $event event object to the HeroComponent deleteHero() method.

The lesson neglects to mention the need to define a Boolean "addingHero" property to the HeroComponent, but of course an IDE like IntelliJ is quick to point that out.

The delete() method of the HeroService starts with an "event.stopPropagation()" statement.  The lesson doesn't explicitly explain why, but the reason it's there is because the delete button in the UI is contained within each hero <li> block, which all have a click event handler that sets the selected hero.  So the stopPropagation prevents the invocation of the click event that would briefly display the mini-detail UI for the selected hero prior to deletion.

This lesson marks the end of the Tour of Heroes tutorial in its current form.  Overall, I thought it was an excellent introduction to the basic elements involved in creating an application with Angular 2.  I was surprised that it didn't include a lesson on forms, but a quick glance at the overall documentation implies that the forms API is still evolving.  I'll have to explore that on my own in the near future.