Building a new component for demonstration purposes

So far, we have built two well-differentiated components we can leverage to deliver a multipage navigation. But in order to provide a better user experience, we might need a third one.

We will create a component in our tasks feature folder, anticipating the form we will use in the next chapter to publish new tasks. Create the following files in the locations pointed out for each one:

app/tasks/task-editor.component.ts

import { Component } from 'angular2/core';
import { ROUTER_DIRECTIVES } from 'angular2/router';

@Component({
  selector: 'pomodoro-tasks-editor',
  directives: [ROUTER_DIRECTIVES],
  templateUrl: 'app/tasks/task-editor.component.html'
})
export default class TaskEditorComponent {
  constructor() {}
}

app/tasks/task-editor.component.html

<form class="container">
  <h3>Task Editor:</h3>
  <div class="form-group">
    <input type="text"
      class="form-control"
      placeholder="Task name"
      required>
  </div>

  <div class="form-group">
    <input type="Date"
      class="form-control"
      required>
  </div>

  <div class="form-group">
    <input type="number"
      class="form-control"
      placeholder="Pomodoros required"
      min="1"
      max="4"
      required>
  </div>

  <div class="form-group">
    <input type="checkbox" name="queued">
    <label for="queued"> this task by default?</label>
  </div>

  <p>
    <input type="submit" class="btn btn-success" value="Save">
    <a href="/" class="btn btn-danger">Cancel</a>
  </p>
</form>

This is the most basic definition of a component, but we will also bring the ROUTER_DIRECTIVES symbol from the router library. This will provide us support, as we will see later on, to include routing directives in our HTML template. This will be used to introduce links in our template to jump to other components, as we will see shortly. Last but not least, we need to expose this new component from our feature folder facade:

app/tasks/tasks.ts

import TasksComponent from './tasks.component';
import TaskEditorComponent from './task-editor.component';
import TaskTooltipDirective from './task-tooltip.directive';

const TASKS_DIRECTIVES: any[] = [
  TasksComponent,
  TaskEditorComponent,
  TaskTooltipDirective
];

export {
  TASKS_DIRECTIVES,
  TasksComponent,
  TaskEditorComponent,
  TaskTooltipDirective
};

Configuring the RouteConfig decorator with the RouteDefinition instances

In order to achieve these goals, we need to start building our top router, which will be in charge of kicking off the routes’ scaffolding. The logical path begins in our top root component. Open its file module and import the following tokens, right next to the ROUTER_PROVIDERS symbol we imported at the beginning of this chapter. The code is as follows:

app/app.component.ts

...
import { 
  ROUTER_PROVIDERS,
  RouteConfig,
  ROUTER_DIRECTIVES 
} from '@angular/router-deprecated';
import { TimerComponent } from './timer/timer';
import { 
  TasksComponent,
  TaskEditorComponent } from './tasks/tasks';
...

The RouteConfig represents the decorator type that will turn our component into a router component. The ROUTER_DIRECTIVES symbol wraps the view directives we will need shortly to link to these routes. We also import the tokens of all the three components we will be dealing with. As we will shortly see, each route needs to declare the type of the component we are routing the browser to.

Let’s continue by replacing the directives in our AppComponent module by the ROUTE_DIRECTIVES symbol, since we will not need to declare the facade tokens of the components that lived in its template anymore. The router will handle this for us:

app/app.component.ts

@Component({
  selector: 'pomodoro-app',
  directives: [ROUTER_DIRECTIVES],
  providers: [SHARED_PROVIDERS, HTTP_PROVIDERS, ROUTER_PROVIDERS],
  template: `
  ...
})

Now, let’s expand the component class definition with the RouteConfig decorator by appending the following decorator right after the @Component decorator block and before the class statement:

app/app.component.ts

@RouteConfig([
  { path: '',
    name: 'TasksComponent',
    component: TasksComponent 
  },
 { 
    path: 'tasks/editor',
    name: 'TaskEditorComponent',
    component: TaskEditorComponent 
  }, {
    path: 'timer',
    name: 'TimerComponent',
    component: TimerComponent
  }
])
export default class AppComponent {}

As we pointed out at the beginning of this chapter, the RouteConfig decorator must be populated with an array of RouteDefinition objects, each one specifying a path that, once reached by the user, will enable the component whose type we have defined in the component property.

Tip

Note: The new Router replaced the @RouteConfig decorator by the @Routes decorator. The name property is removed from the route definitions schema and route matching is performed just by checking the path value.

In the previous example, our host component will react to three different routes and thus serve the TasksComponent item, the TimerComponent item, or the TaskEditorComponent item depending on the route’s path.

Tip

Here we stumble upon another common convention in the previous version fo the Angular 2 router: naming routes with the same name as the component they refer to. As we will see shortly, we will use each route name for populating the links pointing to each resource, so naming routes after the component they will activate becomes pretty useful and intuitive when it comes to assessing the target of each link found in the template. This convention is no longer enforced in the new router, since only paths are used to perform route matching.

There are two questions at this point: where will these components be rendered and how will we trigger each route? In order to answer these questions, we need to look into the router directives in detail.