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.

Wednesday, June 15, 2016

ncline-screenshots: New ncline Module For Managing Screenshot Files

When I'm observing a multi-step process during a debugging exercise, I often take screenshots at each breakpoint.  It gives me a record of the data values and behavior at particular points in the process, sometimes recording something I didn't realize I needed to pay attention to until after the fact.

On Windows computers, I use the freeware application Greenshot to take my screenshots.  One of the features of Greenshot is that it lets you use date and time parameters in the filename settngs, so I have it set to save each screenshot with a name and folder location based on date and time.  If I were to take a screenshot on June 16, 2016 at 3:30pm, I'd end up with a file named "screenshot-15-30-00.png" in a "2016-06-16" subdirectory of my root screenshot directory.

I recently started using a Mac laptop as my primary personal laptop, and I was disappointed to learn that there is no Greenshot version for Macintosh.  OS X's native screenshot implementation is less than ideal:  it saves the screenshot file on the desktop and provides no control over the naming of the screenshot (though it does include the timestamp in the name).  I looked at what Mac-compatible screenshot programs were out there and found many of them had a workflow geared towards manipulating the screenshot right after taking it.  I just wanted to take the screenshot, move on to the next one, and review all of them later.

So I decided to solve my Mac screenshot problem myself.  First, I followed the instructions for changing the location where OS X saves the screenshots (it's not a setting you can change in the OS X UI anywhere) so the files would be created somewhere other than the desktop.  Then I wrote ncline-screenshots, a new, separately-downloaded command module for my Node-powered ncline project.  It uses the chokidar npm module, an enhancement on the native Node file watcher library, to watch the directory where the screenshot files are created.  When a new file is found in that directory, it gets processed by a series of processing rules that rename and/or move the file.

The command module creates the watcher task, provides private functions that implement the processing rules, and includes a few commands for changing configuration settings and rule processing behavior.  Creating additional rules is simply a matter of writing additional functions that manipulate the file and pass on the results of the change to the next rule in the sequence.

Even though it was written to solve a Mac-specific gap in my workflow, ncline-screenshots works on Windows systems as well.