March 2017

Volume 32 Number 3

[The Working Programmer]

How To Be MEAN: Angular Components

By Ted Neward | March 2017

Ted NewardWelcome back, MEANers.

Last month I began my descent into Angular—getting started with it, working with the base template, and taking a quick look at some of the more obvious parts that make up the simple Angular “Hello World” application (msdn.com/magazine/mt793274). It’s clear that Angular (formerly called Angular 2) takes a slightly different approach to building Single-Page Applications (SPAs) than many other JavaScript Web frameworks, including its immediate predecessor, AngularJS (formerly known as Angular 1). But these differences are not just the result of open source peevishness at wanting to do it differently just for the sake of doing it differently; a new approach to building Web applications is building a groundswell of interest, and Angular has chosen to grab on with both hands.

The new word of the Web is “components.”

Components

There are two ways to think about the word components. One is to break down the major parts that make up a traditional Angular application. That list is relatively short and similar to Angular or other Web frameworks:

  • Modules
  • Components
  • Services
  • Routes

A few other parts are lurking in the Angular world, but for the most part, these are the four key components that make up an Angular application.

Modules are the top-level unit of segregation and cognition. Essentially, several modules make up an application, where a module is a container around a reasonably coupled group of smaller parts (components, services and so on). Modules are generally often units of deployment—this is what the browser will download to the user’s machine in order to execute. More important, they’re simply units of cognitive separation in the same way that .NET assemblies are. If all of the code, resources and other pertinent assets pertaining to “viewing and displaying the current time” are grouped together into a single “time” module, developers working with the system don’t have to think about time unless they’re working with (or in) that module.

This reduces the overall cognitive load on the developer trying to keep track of all the moving parts within the system. Obviously, developers are free to put everything into a single module, just as it’s certainly possible to write an entire desktop application in one .NET assembly. However, most developers with any amount of experience under their belts will have some opinions on where those module lines should be drawn, and they’ll modularize their code accordingly. (Whether any two developers agree on where those lines are, on the other hand, is an entirely different story.)

Components are like modules, but writ smaller—where a module may contain all the definitions for doing things relating to HTTP communication (such as Angular’s HttpModule) or HTML forms (FormsModule), components are typically single-purpose and directly related to the UI. In the Hello World application from last month’s column, the code had exactly one component—the AppComponent—which provided a simple, non-interactive text display greeting the world. Despite its simplicity, it was and is a full-blooded Angular component, as evidenced by the fact that the component can be used in a more-or-less opaque fashion, via the <my-app> tag that served as the component’s tag. In essence, the tag usage becomes the client “surface area” for using the component.

Services, on the other hand, are more like low-level libraries that typically provide access to underlying functionality that shouldn’t be a part of the component itself. In an Angular approach, usually making any sort of HTTP API call (such as to the Node/Express/Mongo back end that Microsoft has been hacking out over the last year) should be bundled into a service and simply used from the components that require it. Typically, there will be fewer services than components in a given Angular application, though of course, “your mileage may vary.”

Finally, routes are the primary mechanism of navigation. Routes define the mapping of incoming URL patterns to how the Angular application should respond. Similar in spirit to how Express.js routes were written, routes will be defined in a “routing map” for easy reference from one place, and usually a route will map to a component (though that component in turn might—and often will—make use of other components).

This might seem a little overwhelming, particularly the heavy use of the word “component,” but in reality, it’s simpler than it seems. Let’s start by defining a new component, referenced from the AppComponent application component (sometimes called the “root component” because it’s where the action begins) that displays a name and a button that, when clicked, prints that name to the console. It’s a trivial component, but it’ll serve as an easy way to see how to build a new component, reference it, and use it.

GreetingsComponent

By convention, Angular likes to suffix any component type with the word “component,” so because this component is designed to offer greetings, GreetingsComponent seems like an appropriate—if boring—name for it. It will live in a file called greetings.component.ts, but where that file should live is not an automatic decision; some Angular developers prefer to put each component into its own directory, such as greetings, or some will put it into a components directory. On the other hand, of course, you can always just leave it right next to app.component.ts. Debate rages among Angular developers as to which is the superior option, but for simplicity, we’ll do this as a peer to app.component.ts, so that greetings.component.ts lives directly inside the app directory.

The opening phrases of greetings.component.ts are remarkably similar to the AppComponent from last time:

import { Component } from '@angular/core';
@Component({
  selector: 'greetings',
  template: '<div>Hello</div>'
})
export class GreetingsComponent {
}

It’s really just a simple label. Nothing complex to see here.

Using it, however, means you have to wire up the Greetings­Component into the AppComponent, so that the AppComponent’s HTML will include it, like so:

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1><greetings></greetings>`,
})
export class AppComponent  { name = 'Angular'; }

The only change here is in the “template” metadata on the App­Component. (We’ll find a better place for that to live before too long, don’t worry; you’re not going to have tons of HTML in your component class file.)

If, however, you save the newly modified app.component.ts, Angular will reload (assuming you ran “npm start,” or it was still running from last month), and you won’t see the “Hello” message. This is because the GreetingsComponent isn’t yet loaded, so Angular can’t recognize the <greetings> tag as one that’s placeholding for a component. To do that, you crack open the app module file, app.module.ts, and register GreetingsComponent as one of your own:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { GreetingsComponent } from './greetings.component';
@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, GreetingsComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

The only changes here are to add the “import” that pulls in the GreetingsComponent from disk and to register it as a “declaration” in the @NgModule metadata decorating the AppModule class. Assuming everything is spelled correctly, and the files exist, the text “Hello” appears. Progress!

However, now you need to make some changes to the component itself—and the key here is that the changes are to the component, neatly encapsulated away, and not to anything else.

Adding a Property

First, a generic “Hello” seems lame. Let’s add a field to GreetingsComponent to give it at least some level of personalization:

import { Component } from '@angular/core';
@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.'
})
export class GreetingsComponent {
  constructor() {
    this.name = 'Tarzan'
  }
}

As soon as you save the file, you should see “Hello my name is Tarzan” show up. The “name” property is just a standard TypeScript property—nothing magical there.

However, it would be nice if the name could be determined externally, rather than hardcoded into the class as “Tarzan.” This is the role of the @Input property:

import { Component, Input } from '@angular/core';
@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
}

Notice two things: First, you strongly typed the property name so that TypeScript can help ensure that only strings get passed there; second, @Input() is annotated in front of it. This tells Angular that the actual data for the property should come from the tag usage in a parent component, which you now amend in app.component.ts to look like:

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1><greetings name="Tarzan"></greetings>`,
})
export class AppComponent  { name = 'Angular'; }

Notice that “name” is overloaded here; name is a property used inside of AppComponent, but it’s also a property of GreetingsComponent, and the two are clearly separate. Naturally, if passing the name from AppComponent to GreetingsComponent is the goal, that’s accomplished by using the {{name}} interpolation binding within GreetingsComponent’s template rather than the literal “Tarzan.”

In many cases, the input passed into a component will be something that’s not a string; in those situations, the syntax for a property binding like this takes on a slightly different cast, as in:

<user-profile [user]="currentUser"></user-profile>

Here, the user property is of a non-string type (probably something like UserProfile), and currentUser is the currently authenticated user. You’ll see more of this syntax later, when you start working with non-primitive types (like a Speaker class) to hold the data used by a component.

Adding a Method

Angular components are, at their heart, just TypeScript classes, so it’s easy to add a new method to the class that “does something,” such as print the name to the console:

@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.</div>'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

The sayMyName method does exactly that, just printing the name property to the browser console. However, often these methods will want to be invoked via user action—that means that they have to be wired up to browser events, and that requires another, slightly different, syntax to bind the method to an Angular event, such as a button click. So if the GreetingsComponent defines a button as part of its HTML template, then it can bind the button’s click event to call sayMyName, like so:

@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.' +
    ' <button (click)="sayMyName()">Say my name</button></div>'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

Event-binding syntax looks similar to traditional HTML event binding, except that Angular insists that event binding must be parenthesized (so it’s (click) instead of onClick); property bindings use square brackets, and event bindings use round brackets. It’s a little odd to see at first, but over time it starts to become more comfortable, and dare I say convenient, because now the symbols make it more clear which are properties and which are events.

Removing the HTML

If the thought of the HTML living inside of TypeScript code feels a little odd, the Component lets the template live in an external file, usually following the naming convention of component.html to go alongside component.ts. Therefore, for example, if the Greetings­Component wants to keep its entire HTML in a standalone file, it goes into greetings.component.html, and is referenced using the templateUrl attribute in the @Component metadata on the component itself:

@Component({
  selector: 'greetings',
  templateUrl: './app/greetings.component.html'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

Pay careful attention to the URL used in templateUrl because it’s relative to the root of the application as a whole, so even though greetings.component.html sits right next to greetings.component.ts, the HTML file must be referenced from the parent of the “app” directory.

Wrapping Up

Angular’s approach to building a Web application starts to become clearer: Developers are expected to define smaller-grained components, and then combine them in various ways to form larger-scale applications. It’s certainly not a new concept; in fact, it dates back to the good old days of Visual Basic 3 and the other freewheeling tools of the ’90s. The Angular team has simply brought those concepts forward, into the Web world, and combined them with some more modern tooling to make the experience a little nicer and more Web-like. Certainly, a developer could define the entire application as a single component, but it won’t take long before the pain of trying to do this will vastly outweigh the gain (if there is any—I’m not convinced there would be).

Much remains to be done, but some of the path begins to make it clear: If this application is going to be a database of speakers, then we’ll need to define some SpeakerComponents, at a minimum, to do the display and editing of speakers. Communicating that to the back end will require a service or two. Putting some decorations and lights around the edges will probably be a few mor components, and then some routes to have some URLs that people can use. Hang tight, there’s much more yet to come. In the meantime, happy coding!


Ted Neward is a Seattle-based polytechnology consultant, speaker and mentor. He has written more than 100 articles, is an F# MVP, and has authored and coauthored a dozen books. Reach him at ted@tedneward.com if you’re interested in having him come work with your team, or read his blog at blogs.tedneward.com.

Thanks to the following technical expert for reviewing this article: Ward Bell


Discuss this article in the MSDN Magazine forum