Key Concepts in Angular

Prologue

In my previous post, Getting Started With Angular, I discussed how to quickly set up an Angular project and create a basic, reusable component. In there, I demonstrated the syntax for conditional inclusion (*ngIf), repeaters (*ngFor) and one-way binding and briefly touched on two-way binding.

In this post, I take a step back and discuss some of the key concepts of Angular. Any Angular developer should have a thorough understanding of these concepts.

Components

A Component is a visual element, a user-interface control that has 4 parts:

  1. an HTML file that defines the View,
  2. a LESS (or SCSS/SASS) file that defines optional styling of that view,
  3. a TypeScript (.TS) file that defines the logic that drives that component,
  4. a spec (.SPEC.TS) file that contains the unit test for that component.

Quick notes:

Components are used to display or manipulate data – they should not store the data. This is because a component’s life-cycle is determined by the app’s UI and it may come and go as needed. For example, a panel may exist only to show a message and disappear. Data and state should be controlled by services.

Services

A Service is just a regular TypeScript class that’s used to provide non-view-related functionality and/or store state.

A service can be introduced as a provider or a viewProvider to the component hierarchy. The difference between them is important to know but I will defer it for another post. I will simply refer to both as “provider” from now on.

Service classes are decorated with @Injectable() so they can participate in Angular’s dependency injection system.

Components are typically created and destroyed automatically based on what’s going on with the view. We don’t new the component classes. This is fine for components whose constructors have no arguments, but what if they do? This is where the dependency injection system comes in.

The arguments to the constructor are the class’ dependencies. When a constructor argument is of an injectable type, and that type is declared as a provider for that component or one of its ancestors in the component hierarchy, Angular will pass in the correct service instance. Which one is that? Angular will start at the component’s providers list, and walk up the component hierarchy all the way to the root component (i.e. app.component) and finally to the app.module until it finds the instance. It will fail with a runtime error if it can’t.

This means:

The key takeaway is that a shared service should be defined further up in the component hierarchy but only as high as it’s needed.

Routes

A route is an entry in the routes array defined in app-routing.module.ts. Each entry maps a token of the path part of the URI to a component, and can have child entries. Route ordering is important as Angular matches the URI path top-to-bottom. For example:

const routes: Routes = [
   { path: '', redirectTo: 'welcome', pathMatch: 'full' },
   { path: 'welcome', component: WelcomeComponent },
   { path: 'home', component: HomeComponent, children: [
      { path: 'items', component: ItemsComponent },
      { path: ':id', component: ItemComponent }
   ]}
];

Parent components have a a special HTML element called router-outlet that acts as a placeholder and defines where the child component should live. Note that it’s possible to have multiple router-outlet elements in a component so long as they have unique values for their name attributes. Route entries can target these placeholders with the outlet field.

In the above configuration, a URL like:

More Reading