Saturday, July 9, 2016

Learning Angular 2: Tour of Heroes Tutorial, Lesson 6

The next lesson in the Tour of Heroes tutorial covers an important topic: routing.  I've been looking forward to this particular lesson because I was curious as to how routing would work given the new component-structured architecture.

The first step of the lesson involves renaming the current AppComponent to HeroesComponent (renaming the file, renaming the exported class) and then creating a new AppComponent in preparation for adding routing.  The most interesting part of this step is the relocation of the HeroService from the HeroesComponent providers array to the new AppComponent providers array, and that the use of the service still works in the HeroesComponent (note that both components still import the service module).  The lesson describes this move as the HeroService being "promoted," and it results in the HeroService becoming a singleton shared with the entire app.

Worth noting that the <base> tag for defining the base route in the header is a single, unclosed tag like the <meta> tags.

As I was entering the routes, I noted that IntelliJ provided suggestions for the name of the component.  

Even better, when I selected the component from the suggestion list, IntelliJ automatically added the needed import statement at the top of the app.routes.ts file: very handy.

The first time I looked at this lesson a few days ago, it was written to utilized the recently deprecated version of the router.  It's since been revised to use the latest version of the router, referred to as the Component Router.  In the instructions for setting up the first route to the HeroesComponent, the lesson doesn't explicitly mention a few of the needed modifications to app.component.ts:

  • The import of ROUTER_DIRECTIVES
  • The removal of the HeroesComponent from the directives array and the removal of the HeroesComponent import
  • The addition of ROUTER_DIRECTIVES to the component directives array

...
import { ROUTER_DIRECTIVES } from '@angular/router';
...
directives: [ ROUTER_DIRECTIVES ],
...

...but those modifications do appear in the "v2" code example for app.component.ts somewhat after the fact.

With the browser console open during the reloading of the app, I see error messages generated about the fact that there is no route for '' - that there is no default route defined yet.  Glancing ahead, that is addressed as part of the next step in the lesson with the instructions for setting up the default route to redirect to the DashboardComponent.  Briefly visited the documentation link for redirects and read about how the pathMatch property for the redirect can be set to a value of "prefix" that is useful in other circumstances.

That's when I ran into a little trouble.  I noticed that IntelliJ did not recognized pathMatch as a valid property for a route (it drew a red line under it).  I put the cursor on the "RouterConfig" in the RouterConfig import statement and used the IntelliJ keyboard shortcut Command-B (Control-B on Windows) to jump to the RouterConfig declaration, which lead me to the TypeScript interface definition for a Route object in the RouteConfig array:


export interface Route {
    path?: string;
    terminal?: boolean;
    component?: Type | string;
    outlet?: string;
    canActivate?: any[];
    canDeactivate?: any[];
    redirectTo?: string;
    children?: Route[];
}

Seeing no "pathMatch" property in the interface (interesting that all of the interface properties, including the "path" property, are optional), I decided to check to see what the latest version of the npm "@angular/router" module was:  turned out I had previously updated package.json to install the 3.0.0-alpha.8 version when I first looked over the lesson, but the latest version was now 3.0.0-beta.2.  So I did the upgrade, and the IntelliJ warning disappeared, but when I tried to load Tour of Heroes, I suddenly had errors in the console and the app wouldn't go past the loading screen.  

I revisted the output from "npm-install" and saw warnings about needing the "rc.4" version of other Angular npm modules (core, common, compiler, platform, platform-browser):  my package.json file was configured to pull down version "rc.2" of those files.  

So I updated my package.json dependencies like so...


 "@angular/common":  "2.0.0-rc.4",
 "@angular/compiler":  "2.0.0-rc.4",
 "@angular/core":  "2.0.0-rc.4",
 "@angular/http":  "2.0.0-rc.2",
 "@angular/platform-browser":  "2.0.0-rc.4",
 "@angular/platform-browser-dynamic":  "2.0.0-rc.4",
 "@angular/router":  "3.0.0-beta.2",

...deleted the node_modules folder in my project to install clean, and ran "npm install" again. I got a different set of warnings at the end of the install (so I'll probably be revising my package.json again in the near future), but Tour of Heroes was up and running again, and "pathMatch" was now a property of the Route interface:


pathMatch?: 'full' | 'prefix';

I had already explored how to pull in the HTML of my component from another file using the "templateUrl" property in a previous post, but this lesson officially introduced that technique. Even better, it provided a link to a documentation page about using component-relative paths: if you're using CommonJS modules and certain module loader systems like SystemJS or webpack, you can add a "moduleId" property to your @Component decorator properties and then reference your HTML or CSS files for the component relative to the path, like so:


@Component({
    moduleId: module.id,
    selector: 'my-dashboard',
    templateUrl: 'dashboard.component.html'
})

I like that approach: it makes it a bit more consistent with the path declarations for the imports and it means you can move the component and its HTML/CSS files to another area of your app and not have to update the templateUrl and styleUrl properties.

The second route defined in the lesson demonstrates the use of route parameters, where the route parameters are defined in a route using the colon (:) in the path just like in Angular 1.  I found the fact that components subscribe to route parameter changes on init and then should unsubscribe to them when the component is destroyed (ngOnDestroy) very interesting.

The section of the lesson that adds the mini-detail introduces one of the built-in pipes for Angular 2.  I remember hearing in a recent Adventures in Angular podcast that pipes in Angular 2 are good for doing some - but not all - of the things one used to do in Angular 1 with filters.  They are primarily output formatters, but are not really designed to perform data manipulation functions like sorting.

The router service's navigate() method replaces the old $location service path() method in Angular 1, but rather than taking a single string argument that represents the path to navigate to, it takes an array of values that starts with the route.  It'll be interesting to see how that plays out in larger applications with deep, multi-parameter routes.

Thursday, June 30, 2016

Learning Angular 2: Tour of Heroes Tutorial, Lesson 5

Lesson 5 of the Tour of Heroes tutorial introduces services.

On a recent episode of the Adventures in Angular podcast, guest Pascal Precht made the recommendation that developers should get in the habit of always applying the @Injectable decorator to a service. The reason behind that recommendation is because the decorators (@Component, @Injectable, etc.) cause the emission of metdata needed to work out the dependency injection hierarchy, and there's no negative side-effects to adding the @Injectable decorator to a service even if that service doesn't itself have any dependencies.  The tutorial essentially makes the same recommendation.

The dependency injection (DI) mechanism described in the lesson involves three parts:

  • Using an import statement to import the code of the service module/file

  • Using a constructor function in the component to assign the module variable defined with the import statement as a property of the component.

  • Adding the module variable defined with the import statement to the list of providers in the component's "providers" metadata property, "providing" a working instance of the service.

At first glance, it appears to be a more verbose process than the Angular 1 approach of listing the dependencies as arguments to the module (though if you wanted to do any minification you needed to document the dependencies in either an inline array or a separate $inject property statement).  But you also had to load your modules properly, and of course all of your JavaScript files containing the dependency modules had to be pulled in via <script> tag upfront.  With this syntax the import statements let you only load the dependent modules you need for this particular component.

I think it's a more explicit syntax that takes some of the mystery out of the DI process.  As someone who does a lot of server-side coding, I'm familiar with constructor functions, so for me it's a matter of thinking of the "providers" property of the component as the arguments that will become part of the call to the constructor function generated by the compiler.  You can look in the ES5 output to see the resulting call to the constructor given a provider of "heroService":

var AppComponent = (function () {
    function AppComponent(heroService) {
        this.heroService = heroService;
        this.title = 'Tour of Heroes';
    }

Having hooks into the component lifecycle like ngOnInit provides developers with additional control over their components.  With Angular 1 I would end up working with controllers where it was sometimes hard to distinguish the code that would be executed when the controlller loaded from the code that defined event handlers or watched for certain data changes:  ngOnInit will provide a container for that initialization code, and run it at a point in the lifecycle where the component is "fully engaged."

Given the discussion in lesson 3 about how ngIf destroys/removes the content it contains rather than just hiding it, I was curious to see if that meant that any sub-component in that ngIf-wrapped content would fire off ngOnInit every time it was restored to the DOM.  So I made a really simple HeroSubDetailComponent:

import { Hero } from './hero';
import { Component, Input, OnInit } from '@angular/core';

@Component({
    selector: 'my-hero-subdetail',
    template: `
        <div>
            <span>{{hero.name}} is kinda cool.</span>
        </div>
    `
})

export class HeroSubDetailComponent implements OnInit {
    @Input()
    hero: Hero;

    ngOnInit() {
        console.log( "sub-detail-component initialized" );
    } 
}

...and then added it to the existing HeroDetailComponent within an ngIf block that would only evaluate to true for the first hero:


import { HeroSubDetailComponent } from './hero-sub-detail.component';

@Component({
    selector: 'my-hero-detail',
    directives: [ HeroSubDetailComponent ],
    template: `
    <div>
      <h2>{{hero.name}} details!</h2>
      <div>
        <label>id: </label>
        {{hero.id}}
      </div>
      <div>
        <label>name: </label>
        {{hero.name}}
      </div>
      <div *ngIf="hero.id == 11">
        <my-hero-subdetail [hero]="hero"></my-hero-subdetail>
      </div>
  </div>
    `
})

...and as I suspected, every time the HeroSubDetailComponent was removed then re-added to the DOM as I clicked through different heroes, it would fire ngOnInit on the re-add.

I wasn't aware that Promises were added as native constructs in ES2015 (and thus in the latest implementation of TypeScript), so that was something I learned during this lesson.  I was momentarily confused by how it was used without instantiating an instance of it:  apparently the resolve() method is a static method, so in this case where the data is hard-coded in the application and immediately available it makes sense to go this route.  But a note about the fact that in a more traditional use case you would instantiate a Promise instance might be warranted.

Monday, June 27, 2016

Learning Angular 2: Tour of Heroes Tutorial, Lesson 4

This lesson starts off with creating a new component and a separate Hero class file which is then imported into both of the current components:


import { Hero } from './hero';

Worth noting that './' is relative path syntax that indicates that the file we're importing from lives in the same directory as the current file.  

The manner of embedding the child detail component within the parent component is reminiscent of how custom directives could be added to an existing page in Angular 1, but without the need to handle the data passed into the directive via a scope layer.  

The lesson explains that in order for the hero-detail component to receive the selectedHero from the parent component to use as its internal Hero property, it needs that property identified as an input, preferably with the @Input decorator.  I was curious to see if leaving off the decorator would cause an error.  It doesn't, but it hardly matters since the hero-detail component doesn't show anything if its "hero" property is null.  I did notice that before I added the @Input decorator, IntelliJ had the "hero" property displayed in gray (the default color for indicating an unused variable in IntelliJ), but after I added the annotation IntelliJ changed the color to indicate the variable was being utilized, so there's a hint for the future when looking for silent errors.

The need for the @Input decorator makes sense once you read the documentation linked to the lesson:  it's a way to explictily control which properties in your component can be "targeted" / are made publically accessible to another component.

At the conclusion of the lesson, I wanted to confirm that this method of including the sub-component via a custom tag in the parent component HTML would also work if the HTML was kept in a separate file.  I was pretty sure that one could use a "templateUrl" property in place of the "template" property within a @Component decorator, but typing "templateUrl" in the search box in the right column of the docs area of Angular.io didn't pull up any results.

But I did find an example of it being used in the style guide.  So I moved all of the HTML from app.component.ts into an app.component.html file within the same "app" directory.  But using the same "./" relative path syntax used with the imports didn't work...unless I moved the app.component.html file out of the "app" directory and into the "root" directory with all the configuration files.  If I want to keep the HTML file in the "app" directory, the syntax for the templateUrl needs to be:


templateUrl: 'app/app.component.html'

...which makes sense, as it's a URL path from the base URL of the application.

And yes, as one would expect, the same HTML with the custom sub-component HTML tag works fine when stored in a separate HTML file.  For components you plan to use in multiple projects, it makes sense to embed the HTML in the component itself (less files to copy to each project), and conversely if the component involves a lot of HTML unique to a certain application it's cleaner to store it in a separate file. 

Saturday, June 25, 2016

Learning Angular 2: Tour of Heroes Tutorial, Lesson 3

Building on the lesson 2 coding exercises in the Tour of Heroes tutorial I covered in my last post, the next lesson involves displaying a list of Hero objects.

The "*ngFor" syntax is obviously a departure from the "ng-repeat" syntax of Angular 1.  Less obvious is the fact that now the iteration syntax includes a standard variable declaration ("let").  Leave it off and you get a page-breaking error.  In Angular 1 you could have gotten away with just "hero in heroes".

I glanced at some of the docs for ngFor and saw a number of ideas inherited from ng-repeat:  the ability to grab the index value, the ability to apply certain styles based on an even or odd index value, etc.  But I didn't see any syntax examples for using ngFor to iterate over object literal properties or Maps, something I would do from time-to-time in Angular 1 but would always forget the iteration syntax for.
Curious enough to get sidetracked, I did some searching and learned that currently ngFor doesn't support looping over key/value pair constructs:

https://github.com/angular/angular/issues/2246

...but it sounds like the notion of providing a pipe in the Angular 2 codebase to allow for that is still on the table (a pipe being a function for transforming data values).  And there are a few blog posts out there about writing your own pipe to achieve that functionality.  Something to look into further down the line.

I like the idea of having a "styles" property on a component.  I can see where keeping project-agnostic styles within the component would make it easier to use in multiple projects.

In my previous post I commented on how the initial property assignments in AppComponent (like "title") were untyped.  The "selectedHero" property introduced in this lesson does not have an initial value but is typed as a variable of type Hero.
The new "*ngIf" directive seems straightforward enough, and like the "ng-if" of Angular 1 it can process a compound boolean expression:


<div *ngIf="selectedHero && 1 == 1">

...but seeing it made me curious if the Angular 1 core directives "ng-show" and "ng-hide" also existed in Angular 2 (in my work projects we tended to use those rather than "ng-if" in many places).  Looks like the answer to that is no.  The developer guide page on structural directives like ngIf, ngFor, and ngSwitch makes the point that you're often (though not always) better off using ngIf to destroy the DOM element not in use, thereby removing any of the binding listeners that go with it.

I noticed on that same guide page that the ngSwitch directive was NOT preceded by an asterisk (*) like ngFor and ngIf..and at first glance it seemed like the child ngSwitchWhen directives were not either, until I sat back and realized that in that code example the <template> element the asterisk implies for these directives was "spelled out" in the example.

However, that DOES NOT mean I can do this:


<template ngIf="selectedHero">...</template>

...without the "*", it errors, and with the "*" the browser doesn't know how to render a <template> tag so it isn't displayed....which makes me wonder if the <template> ngSwitchWhen example works....(Chuckle), it does, but I get console messages saying that "ngSwitchWhen is deprecated; use ngSwitchCase instead".  I see there's a pull request to update the docs regarding this change (https://github.com/angular/angular.io/pull/1710).

The last coding step of the lesson:


[class.selected]="hero === selectedHero"

...is technically a class binding, not a property binding as stated in the lesson. And it's limited to assigning one class based on the evaluation of the expression, whereas the ngClass directive supports setting multiple classes.

Wednesday, June 22, 2016

Learning Angular 2: Tour of Heroes Tutorial, Lesson 2

Now that I've gone through the official Angular 2 quick start tutorial, it's time to start the longer "Tour of Heroes" tutorial, starting with lesson 2 (lesson 1 is just an overview of what will be covered in the entire tutorial).

Decided to start with a fresh IntelliJ project in a new "angular-io-tourofheroes" directory. I set up the same way as I did for the quick start tutorial files and copied all of the configuration files and TypeScript files from the quick start project over.  Although not strictly necessary, I changed the "name" value in package.json to "angular2-tourofheroes" before running "npm install".

I once again went into the IntelliJ preferences/settings to have the TypeScript compiler that comes with IntelliJ take care of watching for file changes and compiling on-the-fly...but initially I forgot to check the "Use tsconfig.json" option, so I saw quite a few errors listed in IntelliJ's TypeScript compiler window:

The syntax for setting properties in the AppComponent surprised me a bit:

title = 'Tour of Heroes';

...I would have expected a declaration keyword like "var" or "let", or a key:value syntax instead.  They could be untyped TypeScript class properties:  if I declare the title data type as "String" and then try to assign it the value of 2, I get a TypeScript compiler error.  But if that's the case I would expect to get compiler errors when I go into tsconfig.json and set "noImplicitAny" to "true" instead of "false" (which I would expect to force me to declare data types for all variables and class properties), and that doesn't happen.

Peeking ahead, the Hero class conforms to what I would expect as the property syntax for a TypeScript class, so maybe the Component decorator contains code that allows for a slightly briefer syntax.  Something to look into, I guess.

When I got to the step where the code is switched from one-way data binding to two-way data binding, I made the change manually instead of copying and pasting the code, so I mistakenly kept the interpolated syntax for the hero name:

<input [(ngModel)]="{{hero.name}}" placeholder="name" />

...the result of that was that Angular broke during the rendering process (leaving the "Loading..." message), and in the console in Chrome I got the error "EXCEPTION: Template parse errors: Parser Error: Got interpolation ({{}}) where expression was expected at column 0 in [{{hero.name}}]".  Removing the curly braces from around "hero.name" of course fixed the problem.

The syntax of "[(ngModel)]" seems like an odd syntax at first until you read the background behind it:  I think a sentence or two about the fact that it's shorthand for combining two separate bindings - the data binding with "[ ]" and and change event binding with "( )" - is warranted here for those folks who don't follow the reference links to the guide chapters and find the syntax a turn-off.  Still, I sense a keyboard shortcut for inserting "[(ngModel)]" into my code is in my future.

It looks like you can also do one-way data binding on the value of an input by just using [ngModel]. This code:

<input [ngModel]="hero.name" placeholder="name" />

...still displays the name value in the input, but just like when using interpolation changing the name in the input does not change it in the model. Yet another reason for a keyboard shortcut for the two-way binding: it would be easy to miss adding the inner "( )" as you're typing, and you wouldn't get an error but you also wouldn't get the two-way binding you expect.

So ends the first coding lesson.

Monday, June 20, 2016

Learning Angular 2: The Official Quick Start guide

I'm starting my Angular 2 journey with the official quick start guide on the Angular 2 website, https://angular.io/.  I'm going to use the TypeScript version of the quick start guide, since I plan on coding Angular 2 with TypeScript.  

I already have Node installed on my Mac laptop via nvm (Node Version Manager), so that's taken care of.

I want to figure out any nuances involved in using IntelliJ as my IDE for coding Angular 2 (it SHOULD work similarily to how WebStorm works in the regard), so I want to create a new IntelliJ project to house the quick start files.  Did a quick search for any IntelliJ plugins that would semi-automate the setup of a new Angular 2 codebase in an IntelliJ project, but didn't find any.  Probably just as well:  I expect to start using the Angular CLI tool for that sort of thing as I get further along anyway.  So I created a new project in IntelliJ pointed to my "angular-io-quickstart" directory.  IntelliJ expects all project files to live in modules, so I created a static web module called "root" (a module choice that doesn't come with any preconfigured structure or code files) which will hold all of the project files.  I then created all of the configuration files (package.json, tsconfig.json, etc.) in that root module.

I then ran "npm install" from the angular-io-quickstart/root directory.  A "typings" folder was created along with a "node_modules" folder, and there were "typings" modules in "node_modules", so I didn't need to execute the "npm run typings install" command cited in the guide.

The guide does a good job of explaining the different pieces of the app.component.ts file, but a few extra notes:

  • The import statement is a conventional TypeScript import statement (it's not something unique to Angular 2), so further information about its syntax can be found by searching for TypeScript articles on the web.

  • Decorators are also not exclusive to Angular:  they are also used in some server-side languages as a means of adding new properties and/or functionality to an object without using extension.

When I added the main.ts file as described, IntelliJ drew a red line under the AppComponent argument for the bootstrap function in the final line.  Hovering over the argument displayed the following error "Argument AppComponent is not assignable to parameter type Type".  The error did not prevent me from running the quick start once I finished the rest of the instructions, however.

Did some research and the solution for that is to go into the IntelliJ settings  (File/Settings in Windows / IntelliJ/Preferences in OS X), open up the Language & Frameworks settings, and in the TypeScript settings check the "Enable TypeScript Compiler" and "User tsconfig.json" options.  Reference:  http://stackoverflow.com/questions/34366797/angular-2-bootstrap-function-gives-error-argument-type-appcomponent-is-not-assi

The IntelliJ TypeScript settings also include a "Track changes" option.  With that option turned on, I don't need the compiler watcher turned on as part of the "npm start" command in the quick start instructions:  I can simply use "npm run lite" to get just the lightweight web server running, and IntelliJ will make sure my changes are picked up.  Using both "Track changes" and "npm start" together doesn't produce any sort of conflict or error however.

In either case, whether invoking the stand-alone TypeScript compiler (tsc) via the "npm start" command or via the built-in TypeScript compiler in IntelliJ, you end up generating a ".js" and "js.map" file for each ".ts" file in the project (though when IntelliJ creates those files, you can visually collapse the generated files under the TypeScript file that generated them).  I imagine that any build process that gatheres the code needed to deploy a project to production would exclude the ".ts" files (not sure whether the ".map" files would need to live on production as well).

The index.html file is pretty straightforward with the exception of the System.import statement.  If I understand things correctly, in the systemjs.config.js file:
  • The 'app' entry in the "map" object literal tells SystemJS what directory contains the 'app' modules (in this case a directory of the same name).

  • The 'app' entry in the "packages" object literal tells SystemJS that when System.import() is used with the argument 'app' (with no filename specified), it should execute the 'main.js' file...which SystemJS knows from the "map" lives in the "app" directory.

A brief consultation of the configuration API for SystemJS seems to confirm all that.

Overall, the quick start guide was easy to follow and well-explained.  Then again, it IS essentially the "Hello World" example for Angular 2, so it isn't supposed to be hard.  

Next step:  starting to work through the "Tour of Heroes" tutorial.

Learning Angular 2: The Journey Begins

Over the past few years, my blog posts have been few and far between.  Partly because of the pressure I'd put on myself to write very polished, very "correct" posts, which for me takes a lot of effort and energy, and partly because I know that I don't have a consistent audience...which doesn't inspire a great deal of motivation to make the effort to create the posts I'm used to writing.

So I'm going to try something a bit different.  I'm going to try writing a series of blog posts documenting my experience learning Angular 2.  When I'm learning new technology or working through a coding project, I tend to take my own "stream of consciousness" notes as I go anyway, so the plan is to write blog posts instead of notes, writing them candidly and "in the moment" as much as possible so I don't get hung up self-editing as I go.  I will undoubtedly "mislearn" (and hence mis-post) things as I go, but I need to be okay with that:  mistakes are a part of learning.  And I think, mistakes or not, my posts will be of some value to other folks trying to learn Angular 2.

I come to Angular 2 with a mix of ignorance and experience.  I've worked with JavaScript for many years, initially coding with "raw" JavaScript, and then with jQuery and jQuery UI, and eventually with Angular 1.  I"m experienced in making it do what I need it to do, but I wouldn't say I"ve ever had a deep understanding of its inner workings.  I'm comfortable coding in Angular 1, but rusty because I haven't had the opportunity to do any serious coding in it in awhile.  What code I've been writing during my day job lately has been server-side code in Groovy or Java.

I'm a bit more ignorant in the Angular 2 "companion" technologies.  I've used Grunt in some personal projects, so I have some familiarity with using a task manager as a build tool.  I've run browserify once or twice, but I'm hardly comfortable with it; I've never run webpack.  I've played with Node, so I'm familiar with npm and the notion of pulling in modules with require statements.  I took the TypeScript course on Pluralsight, so I know what the deal is there even though I haven't used it yet.

My hope is that this mix of ignorance and experience translates to blog posts that provide some value to both readers who are very new to JavaScript/client-side programming in general as well as to those readers who are already experienced (if not yet expert) client-side developers.

Anyway, enough of this introductory talk:  time to get conclude this post and start learning.