30 seconds of angular
Star
✨✨ New to Angular? Check out interactive Angular Codelab ✨✨

Bind to host properties with host binding

Every rendered angular component is wrapped in a host element (which is the same as component's selector).

It is possible to bind properties and attributes of host element using @HostBinding decorators, e.g.

import { Component, HostBinding } from '@angular/core';
    
    @Component({
       selector: 'my-app', 
       template: `
        <div>Use the input below  to select host background-color:</div>
        <input type="color" [(ngModel)]="color"> 
      `,
      styles: [
        `:host { display: block; height: 100px; }`
      ]
    })
    export class AppComponent {
      @HostBinding('style.background') color = '#ff9900';
    }
    

Component level providers

Generally we get one service instance per the whole application.
It is also possible to create an instance of service per component or directive.

@Component({
      selector: 'provide',
      template: '<ng-content></ng-content>',
      providers: [ Service ]
    })
    export class ProvideComponent {}
    
@Directive({
      selector: '[provide]',
      providers: [ Service ]
    })
    export class ProvideDirective {}
    

Links

Getting components of different types with ViewChild

It's possible to use @ViewChild (also @ViewChildren and @ContentChild/Children) to query for components of different types using dependency injection.

In the example below we can use @ViewChildren(Base) to get instances of Foo and Bar.

abstract class Base {}
    
    @Component({
      selector: 'foo',
      providers: [{ provide: Base, useExisting: Foo }]
    })
    class Foo extends Base {}
    
    @Component({
      selector: 'bar',
      providers: [{ provide: Base, useExisting: Bar }]
    })
    class Bar extends Base {}
    
    // Now we can require both types of components using Base.
    @Component({ template: `<foo></foo><bar></bar>` })
    class AppComponent {
      @ViewChildren(Base) components: QueryList<Base>;
    }
    

Links

Global event listeners

It is possible to add global event listeners in your Components/Directives with HostListener. Angular will take care of unsubscribing once your directive is destroyed.

@Directive({
      selector: '[rightClicker]'
    })
    export class ShortcutsDirective {
      @HostListener('window:keydown.ArrowRight')
      doImportantThings() {
        console.log('You pressed right');
      }
    }
    

Bonus

You can have multiple bindings:

@HostListener('window:keydown.ArrowRight')
    @HostListener('window:keydown.PageDown')
    next() {
      console.log('Next')
    }
    

You can also pass params:

@HostListener('window:keydown.ArrowRight', '$event.target')
    next(target) {
      console.log('Pressed right on this element: ' + target)
    }
    

Loader Component

You can create own helper component and use it instead of *ngIf.

@Component({
      selector: 'loader',
      template: `
        <ng-content *ngIf="!loading else showLoader"></ng-content>
        <ng-template #showLoader>🕚 Wait 10 seconds!</ng-template>
      `
    })
    class LoaderComponent {
      @Input() loading: boolean;
    }
    

For usage example:

<loader [loading]="isLoading">🦊 🦄 🐉</loader>
    

Note that the content will be eagerly evaluated, e.g. in the snippet below destroy-the-world will be created before the loading even starts:

<loader [loading]="isLoading"><destroy-the-world></destroy-the-world></loader>
    

Links

Renaming inputs and outputs

In certain cases @Input and @Output properties can be named differently than the actual inputs and outputs.

<div 
      pagination 
      paginationShowFirst="true"
      (paginationPageChanged)="onPageChanged($event)">
    </div>
    
@Directive({ selector: '[pagination]'})
    class PaginationComponent {
      @Input('paginationShowFirst') 
      showFirst: boolean = true;
    
      @Output('paginationPageChanged') 
      pageChanged = new EventEmitter();
    }
    

Note: Use this wisely, see StyleGuide recommedation

Links

hammerjs-gestures

To act upon swipes, pans, and pinhces as well as the other mobile gestures, you can use hammerjs with HostListener decorator, or an event binding,

npm install hammerjs
    
@HostListener('swiperight')
    public swiperight(): void {
      // Run code when a user swipes to the right
    }
    

Links

Bonus

Here are samples on how to use all of the hammerjs event bindings, you can use these events with a HostListener as well:

  <!-- pan events -->
      <div (pan)="logEvent($event)"></div>
      <div (panstart)="logEvent($event)"></div>
      <div (panmove)="logEvent($event)"></div>
      <div (panend)="logEvent($event)"></div>
      <div (pancancel)="logEvent($event)"></div>
      <div (panleft)="logEvent($event)"></div>
      <div (panright)="logEvent($event)"></div>
      <div (panup)="logEvent($event)"></div>
      <div (pandown)="logEvent($event)"></div>
    
      <!-- pinch events -->
      <div (pinch)="logEvent($event)"></div>
      <div (pinchstart)="logEvent($event)"></div>
      <div (pinchmove)="logEvent($event)"></div>
      <div (pinchend)="logEvent($event)"></div>
      <div (pinchcancel)="logEvent($event)"></div>
      <div (pinchin)="logEvent($event)"></div>
      <div (pinchout)="logEvent($event)"></div>
    
      <!-- press events -->
      <div (press)="logEvent($event)"></div>
      <div (pressup)="logEvent($event)"></div>
    
      <!-- rotate events -->
      <div (rotate)="logEvent($event)"></div>
      <div (rotatestart)="logEvent($event)"></div>
      <div (rotatemove)="logEvent($event)"></div>
      <div (rotateend)="logEvent($event)"></div>
      <div (rotatecancel)="logEvent($event)"></div>
    
      <!-- swipe events -->
      <div (swipe)="logEvent($event)"></div>
      <div (swipeleft)="logEvent($event)"></div>
      <div (swiperight)="logEvent($event)"></div>
      <div (swipeup)="logEvent($event)"></div>
      <div (swipedown)="logEvent($event)"></div>
    
      <!-- tap event -->
      <div (tap)="logEvent($event)"></div>
    

ng-content

With ng-content you can pass any elements to a component.
This simplifies creating reusable components.

@Component({
      selector: 'wrapper',
      template: `
        <div class="wrapper">
          <ng-content></ng-content>
        </div>
      `,
    })
    export class Wrapper {}
    
<wrapper>
      <h1>Hello World!</h1>
    </wrapper>
    

Links

trackBy in for loops

To avoid the expensive operations, we can help Angular to track which items added or removed i.e. customize the default tracking algorithm by providing a trackBy option to NgForOf.

So you can provide your custom trackBy function that will return unique identifier for each iterated item.
For example, some key value of the item. If this key value matches the previous one, then Angular won't detect changes.

trackBy takes a function that has index and item args.

@Component({
      selector: 'my-app',
      template: `
        <ul>
          <li *ngFor="let item of items; trackBy: trackByFn">{{item.id}}</li>
        </ul>
      `
    })
    export class AppComponent { 
      trackByFn(index, item) {
        return item.id;
      }
    }
    

If trackBy is given, Angular tracks changes by the return value of the function.

Now when you change the collection, Angular can track which items have been added or removed according to the unique identifier and create/destroy only changed items.

Links