Chapter 4. Components

Components are Angular 2 building blocks that contain both logic and UI behavior. Components contain both HTML templates and classes. You can say that component classes are like Angular 1.x controllers.

You can compare components to an Angular 1.x directive, however, a definition of a component is one autonomic module that contains both UI and UI-oriented logic. If you are familier with Angular 1.x, you may compare a controller to a small module that contains both an HTML template and a controller, all while the exported class acts as the controller.

Components in Angular 2.0 increase the HTML element name vocabulary with the app’s components, in the same way that HTML5 web components are supported in Angular 2.0.

While building a component we will provide it with both HTML template and UI related logic. The UI related logic is on a class that we export. The class is decorated with a component decorator function that receives an object with the HTML template as one of its members.

Component Decorator

Component Decorator A component contains an exported class decorated with a component decorator. A component decorator is a function that gets a ViewMetaData typed object as a parameter.

When creating a component, we have to decorate our class with the component decorator. The component decorator is a JavaScript function that receives a component description object as a parameter. This object is of type ViewMetaData and contains the following properties:

  1. selector : The selector for the component. It can be a new HTML element name, attibute, class, etc. In most cases, the selector is set for a new HTML element name.
  2. template : an HTML markup to be rendered when the component is set.
  3. templateUrl: A URL points at an HTML file that contains the template.
  4. directives: An array of types of other components or directives used by this component.
  5. pipes: An array of pipes used by the component as pipes (See pipes later on in this chapter)
  6. Styles: An array of strings where each string is a style rule
  7. styleUrlS: A URL to an external CSS file
  8. encapsulation: The encapsulation type of the view
  9. providers: An array of types of injectable services used by the components, so whenever the component is instanciated, an instance of each provider is instanciated as well

A component is structured as follows:

import {Component} from '@angular/core'
/**
Another imports
**/ 
@Component({/*ViewMetaData object */})

export class MyCompoentClass {
}

The component template The component template may be written inside the component decorator in the ViewMetaData’s template member, or written in a seperate file while its URL is given by the templateUrl member of the ViewMetaData object.

Please note that the template URL is relative to the application root and not to the component.

ViewEncapsulation ViewEncapsulation is an enumaration used to determine how styles will be encapsulated in the component. It has three entries:

  1. Native: Use the browser’s native encapsulation mechanism. This means using Shadow DOM for the component and create a Shadow root on the component’s host element.
  2. Emulated: Emulates the native encapsulation. The emulation is done by adding a surrugate ID to the host element and preprocess the style rules given on the ViewMetaData object of the component. This is the default option and it will be used unless stated otherwise.
  3. None: No style is encapsulated for the component.

Component Usage:

Component Usage:

Using a component is enabled by importing the component’s class, by adding the component’s selector in the suitable place in the template using the component, and by adding the component’s class to the array of directives used by the host compoents. You may add some attribute to the same hosting element to send data to the component. We will later demonstrate it in the Input/Output section in this chapter.

Using injectable services

Using injectable services

This is a component that wishes to use injectable service needs to receive it in one of its constructor’s parameters. Having the private keyword before each injectable parameter makes a class member named the same as the parameter name and assigns the parameter to it.

Providers: Having the constructor parameters doesn’t make sure that in instance will be sent to the constructor. To make sure in instance is created, we need to put the injectable service type in the providers array of the ViewMetaData .

Note: Unlike Angular 1.x services, the Injectables services are not necessarily singletons. Whenever a type of service is in a component’s providers array, an instance of it is created upon the creation of the component’s instance (i.e., it is not a singleton). It will be preferred that injectable services you wish to use as a single instance will be put in one provider’s array in the application’s main component.

The code example below demonstrates how to declare an injectable service and how to use it.

First, let’s write an injectable service:

@Injectable()
export class MyService {
/*Service code goes here*/
}

As you can see above, this is simply an exported class decorated with the Injectable decorator.

Second, let’s use it in a component:

First, we have to import the class, because we will always do this when we want our code to use code from another file:

import {MyService} from 'path/to/myService';

Second, we need to instansiate it. The Angular 2.0 framework will do that for us after we will put our service in a component’s providers array. If the service is put in the provider’s array in another component (for example, our main component), the service has to be imported there as well.

@Component({ /*some ViewMetaData members */
   providers: [ MyService],
   /*some other ViewMetaData members */ });

And, last but not least, we have to inject the service in our component’s class constructor.

export class MyCompoentClass {
 constructor(private _myService:MyService){
 }
}

By doing so we create a class member named _myService with the type of MyService, and the parameter sent to the contructor is assigned to it.

Plesae note that it creates a class member without you ddeclaring it, No additional code is needed here.

Pipes

When binding data on our HTML template, we might want to present the data in a different form rather than just the data itself (meaning that Angular will use the _toString method on the presented object).

Therefore, we can run the data through a pipe.The syntax for doing so is to add a pipe sign (|) after the binding expression and use a pipe.

We might add some more argument to the pipe using the colon mark (:) before each argument we add.

We will need, of course, to add the pipe’s type to our pipes array member of our component’s ViewMetaData after importing it.

Built in pipes

Here are some built in pipes, which all may be imported from '@angular/common’;

  1. UpperCasePipe (name: ‘uppercase') returns an upper-case string
  2. LowerCasePipe (name: ‘lowercase') returns a lowe-case string
  3. ReplacePipe (name: ‘replace') receives pattern and replacement as arguments, returns a string after replacement is done. The pattern can be a string or a regualr expression, the replacement can be a string or a function
  4. DatePipe (name: ‘date') receives pattern as argument and returns a formatted date according to the given pattern, or to the browser’s default locale if no pattern has been given

Writing custom pipes

A pipe is also a class, however, this class implements the PipeTransform interface that has a method named transform, which is the one who does the formatting work.

To demonstrate, here is a simple pipe that reverses its given source:

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({name: 'reverse'})
export class ReversePipe implements PipeTransform {

    transform(source:any) {
        if (!source || !source.length){
            return source;
        }
        let result = '';
        for (let i = 0; i < source.length; i++)
       {
          result+= s[s.length - i - 1];
       }
       return result;
    }
}

To use the pipe in our component we will import it, put its type in our pipes array on our ViewMetaData parameter sent to the Component decorator, and put its name wherever we need in our template.

/*Import the child component */

import {ReversePipe} from 'path/to/reversepipe';

@Component({selector: 'main-component',
template: `<h1>{{compoenntName}}</h1>
         <!-- and now in reverse -->
         <h2>{{compoenntName | reverse}}</h2>
         `

 pipes: [ReversePipe]
 })
 export class MainComponent{
     private componentName: string;

     constructor(){
      this.componentName = 'Main app component';
     }
 }

Directives

Directives

Using components in another one

In case you wish to use another components or directives in your component, all types of components/directives you wish to use has to be written in the directive array member of the ViewMetaData object. After doing that, all you need to do is simply put the directive.

For that, you need to do the following:

  1. Import the other component / directive type.
  2. Put the type you’ve imported in the directives array member of the ViewMetadata object.
  3. Use the directive / component as stated in its selector.

In the following example, let’s create a simple component and use it on another one.

This is the child component we’ll use:

import {Component} from '@angular/core';

@Component({selector: 'child-component',
 template: `<h2>This is an child component</h2>`
 })
 export class Childomponent{
 }

Now, let’s use it in another component:

/*Import the child component */
import {Childomponent} from 'path/to/childComponent';

@Component({selector: 'main-component',
template: `<h1>Main app Component</h1>
         <!-- using the child cmponent here -->
         <inner-component></inner-component>
         `
         /*Putting the child compont type in our directives*/
 directives: [Childomponent]
 })
 export class MainComponent{
 }

Note that forgetting to put the type in your directives array member won’t produce any error messages on your console, but won’t render your component.

Inter-component communication

You can pass data to and from a component you’re using. In order for our inner component to receive data to one of its class members, this class member is to be decorated by the Input decorator.

To demonstrate it, let’s alter our inner component a little bit.

import {Component, Input} from '@angular/core';

@Component({selector: 'child-component',
 template: `<h2>{{componentName}}</h2>`
 })
 export class ChildComponent{
 @Input() componentName:string;
 }

We have added a property named componentName to the inner component preceded with the Input decorator, imported also from the @angular/core.

To show it, we’ve bounded it to the inner component template.

Now, all we need to do, is bind the componentName to a member in the main component.

/*Import the child component */
import {ChildComponent} from 'path/to/childComponent';

@Component({selector: 'main-component',
template: `<h1>Main app Component</h1>
         <!-- using the inner cmponent here -->
         <child-component [componentName]="childComponentName"></child-component>
         `
         /*Putting the child compont type in our directives*/
 directives: [Childomponent]
 })
 export class MainComponent{
   childComponentName: string;

   constructor(){
    this.childComponentName = "child component";
   }
 }

Using Events

After discussing the method of the parent component way to send data to its child components, we need to pass data in the opposite direction. Because the child component doesn’t know which component is going to contain it, it cannot bind to any member of its containing component. Therefore, the only way a child component can communicate with its parent is through Events.

For that matter we will use another decorator named Output and a class named EventEmitter for launching the event.

The EventEmitter is a generic type where the type parameter is the type of the argument sent with the event.

EventEmitter class

We’ve previously dicussed how to use this class, so now we will elaborate a little more about that class.

This class extends the Subject class from rx.js, which extends the Observable class of the same module. You may read more about that in Chapter 10, Change Detection.

Whenever you use the event on your HTML template you actually subscribe on the event, and your handler is added to its observers array. All observers are called when using the event’s emit() method.

Built in Events

As discussed on the Data Binding section, the event is bound by round brackets. There are many built in events, however, on our component we are going to create a custom event.

We will firs add a textbox to the child component and bind it to another property.

Second, we will create an EventEmitter typed member that will invoke an event whenever the newly created property is changed.

Third, we will bind the change event of the input element to a method on the child component in that method, where the event we’ve just created will be launched.

import {Component, Input, Output} from '@angular/core';

@Component({selector: 'child-component',
 template: `<h2>{{componentName}}</h2>
        <input type="text" [(ngModel)]="comopnentValue" (change)="componentValueChanged()"/>
      `
 })
 export class ChildComponent{
 @Input() componentName:string;
 @Output() onComponentValueChange:EventEmitter<any> = new EventEmitter<any>();

 componentValue: string;

 componentValueChanged(){
     this.onComponentValueChange(this.componentValue);
 }

 }

After doing that, on our main component all we have to do is to bind to the newly created custom event. We will add another string member to our main component class, named childComponentValue and bind it to a span’s inner text on our main component, so the changes will be shown on the screen.

/*Import the child component */
import {ChildComponent} from 'path/to/childComponent';

@Component({selector: 'main-component',
template: `<h1>Main app Component</h1>
         <!-- using the inner cmponent here -->
         <child-component [componentName]="childComponentName " (onComponentValueChange)="childComponentValueChange($event)">
         </child-component>
         <!-- showing child component value on the main component-->
         <span>{{childComponentValue}}</span>
         `
         /*Putting the child compont type in our directives*/
 directives: [Childomponent]
 })
 export class MainComponent{
   childComponentName: string;
   childComponentValue: string;

   constructor(){
    this.childComponentName = "child component";
   }

   childComponentValueChange(componentValue){
     this.childComponentValue = componentValue;  

   }
 }

Now we have created a custom event that will be fired from the child component to its parent whenever the componentValue is changed on the child component.

Component lifecycle

A component has its own lifecycle events so we can hook them and right the code that will run whenever these events occur. Angular manages the lifecycle itself so we can have our custom code.

For hooking an event our component class should implement the corresponding interface. Angular core has a corresponding interface for each event. That interface has a method with the ng prefix and the event name (for example ngOnInit , ngOnDestroy etc.).

Here’s a list of lifecycle hooks

  1. OnInit occurs after the compoenent is being initalized
  2. OnDestroy occurs before a component is destroyed
  3. OnChanges occurs on every change that occurs in the component data
  4. DoCheck extends the Onchanges handler, and when DoCkeck is implemented, it overrides the default change detection algorithm
  5. AfterContentInit occurs after content init

In order to hook one of these lifecycle events you should implement its corresponding interface (inerface names are listed above). Each interface has a method that will handle the lifecycle event.

Summary

Components hold the UI structure and behavior of our application. They can be built on the HTML5 web component wherever the browser supports them. A component can contain another component and bind data to it by its @Input decorated members. The inner component can send messages to its container component by using custom events. Cutom events are written with EventEmitter<type> type members decorated with the @output decorator.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset