Quick ref

Conditional attribute

[attr.disabled]="name.value == whatever ? true : null"

@Input

Passing data into an Angular component.

import {Input} from '@angular/core';
@Input('srvElement') element: {type: string, name: string, content: string};
@Input() name: string;


<app-example 
    *ngFor="let whatever of whatevers"
    [srvElement]="whatever" 
    [name]="whatever.name">
</app-example>

@Output

Listen in the parent for when some data changes in the child.

import { EventEmitter, Output } from '@angular/core';
@Output() serverCreated = new EventEmitter<{serverName: string, serverContent: string}>();
@ViewChild('nameInput', { static: false }) nameInput: ElementRef;
@Input() name: string;


onWhatever(nameInput: HTMLInputElement) {
    this.serverCreated.emit({
        serverName: nameInput.value,
        serverContent: this.serverContentInput.nativeElement.value
    });
}
<input
      type="text"
      class="form-control"
      #nameInput>

<button (click)="onWhatever(nameInput)">Add Server</button>

@ViewChild

Provide access to child elements in the view DOM by setting up view queries

<form (ngSubmit)="onSubmit()" #f="ngForm">
// app.component.ts
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';

@ViewChild('f', { static: false }) signupForm: NgForm;

onWhatever() {
    this.signupForm.form.patchValue({
        userData: {
            username: suggestedName
        }
    });
}

Model

  • Maintainability
  • Extensibility
  • Ensures the type has been defined (typescript)
export class Ingredient {
  constructor(public name: string, public amount: number) {}
}

import { Ingredient } from '../shared/ingredient.model';

ingredients: Ingredient[] = [
    new Ingredient('Apples', 5),
    new Ingredient('Tomatoes', 10),
];

Directives

Directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler to attach specified behavior to that DOM element (e.g. via event listeners), or even to transform the DOM element and its children.

Custom directives

Angular Custom Directives

import { Directive, ElementRef, Renderer2, Input, OnInit } from '@angular/core'

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective implements OnInit {
  @Input() defaultColor: string

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {}

  ngOnInit() {
    if (this.defaultColor) {
      this.setBgColor(this.defaultColor)
    } else {
      this.setBgColor('white')
    }
  }

  setBgColor(color: string) {
    this.renderer.setStyle(
      this.elementRef.nativeElement,
      'backgroundColor',
      color
    )
  }
}
<button appExample defaultColor="red">Button</button>

Services and dependency injection

Custom Service

A service is an object that is available for use throughout the application. It can perform various taks, for example: such as collecting and manipulating data, logging info etc

export class ExampleService {
    count = 0;

  log() {
    this.activeToInactiveCounter++;
    console.log('I am logging');
  }

  incrementCount() {
    this.whatever++;
    console.log('Count ' + whatever);
  }
}


import { UserService } from '../users.service';

export class ActiveUsersComponent implements OnInit {
  users: string[];

  constructor(private ExampleService: ExampleService) {}

  ngOnInit() {
    this.count = this.ExampleService.count;
  }

  onClick() {
    this.ExampleService.incrementCount();
  }
}


import { CounterService } from './counter.service';

@NgModule({
  providers: [CounterService]
})

Routing

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HomeComponent } from './home/home.component';

const appRoutes: Routes = [
  { path: '', component: HomeComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})
export class AppRoutingModule {

}

Guards

canActive - determine if a route can be activated.

canActiveChild - determine if a child route can be activated.

canDeactivate - determine if a guard can be deactivated (exited).

Observables

Angular makes use of observables as an interface to handle a variety of common asynchronous operations. For example:

  • You can define custom events that send observable output data from a child to a parent component.
  • The HTTP module uses observables to handle AJAX requests and responses.
  • The Router and Forms modules use observables to listen for and respond to user-input events.

You can see an example of eventEmitter in the Output section

Event emitter or subjects

  • EventEmitter extends Subject
  • The Angular2 team stressed the fact though, that EventEmitter should not be used for anything else then @Output()s in components and directives.
  • Also EventEmitter gets cleaned up automatically unlike custom Subjects that you need to unsubscribe to in the onDestroy lifecycle hook.

Subjects

  • Subject ...only upcoming values
  • BehaviorSubject ...one previous value and upcoming values
  • ReplaySubject ...all previous values and upcoming values
  • AsyncSubject ...the latest value when the stream will close

An RxJS Subject is a special type of Observable that allows values to be multicasted to many Observers. Use services over EventEmitters when communicating across components which are subscribed() somewhere. Don't use them when you're using @output.

// user.service
import { Subject } from 'rxjs';

export class UserService {
  activatedEmitter = new Subject<boolean>();
}
// app.component.ts
import { UserService } from './user.service';
export class AppComponent implements OnInit, OnDestroy {
  userActivated = false;
  private activatedSub: Subscription;

  constructor(private userService: UserService) {
  }

  ngOnInit() {
    this.activatedSub = this.userService.activatedEmitter.subscribe(didActivate => {
      this.userActivated = didActivate;
    });
  }

  ngOnDestroy(): void {
    this.activatedSub.unsubscribe();
  }
}
onActivate() {
    this.userService.activatedEmitter.next(true);
}

Template driven forms

Angular infers the Form object from the DOM. Template-driven forms rely on directives in the template to create and manipulate the underlying object model. They are useful for adding a simple form to an app, such as an email list signup form. They're easy to add to an app, but they don't scale as well as reactive forms. If you have very basic form requirements and logic that can be managed solely in the template, template-driven forms could be a good fit.

Reactive forms

Form is created programatically and synced with the DOM. Reactive forms provide direct, explicit access to the underlying forms object model. Compared to template-driven forms, they are more robust: they're more scalable, reusable, and testable. If forms are a key part of your application, or you're already using reactive patterns for building your application, use reactive forms.

Pipes

Reactive Templates with ngIf and the Async Pipe - blog.angular-university.io

In cases where async data is pulled - for example using the HttpClient.get() - this will return an observable. You can make use of the Async pipe to reactively load parts of an app.

@Component({
  selector: 'app-root',
    template: `
    <div class="course-detail" *ngIf="courseObs | async as course; else loading">
        <div class="course-field">
            {{course.shortDescription}}
        </div>
        <div class="course-field">
            {{course.longDescription}}
        </div>
        <div class="course-field">
            {{course.duration}}
        </div>
        <div class="course-field">
            {{course.flags}}
        </div>
    </div>
    <ng-template #loading>
        <div>Loading ...</div>
    </ng-template>    
`})
export class AppComponent implements OnInit {

    courseObs: Observable<Course>;

    constructor(private courseService: CourseService) {
    }

    ngOnInit() {
        this.courseObs = this.courseService.loadCourse(1);
    }

}
@Injectable()
export class CourseService {

    constructor(private http:HttpClient) { 
    }
    
    loadCourse(id:number) {
        return this.http.get<Course>(`/courses/${id}`);
    }
}

interface Course {
    id:number;
    shortDescription:string;
    longDescription?: string;
    duration?:number;
    tags?: string[];
}

Breakdown

  • the async pipe is being used to subscribe only once to courseObs
  • the else clause is defining what to display while the data is not available (the ng-template named loading)
  • the 'as' syntax is specifying a template variable for the expression courseObs | async, and that variable is named course
  • the result of this expression is aliased as course, and corresponds to the course object emitted by the course observable
  • now there is a local course variable available inside the ngIfsection, that corresponds to the value emitted by the backend call
  • This course variable is ready to be used, just like if the course object had been passed synchronously as an @Input() to this component

Advantages

  • There are no manual subscriptions at the component level for observables coming out of the service layer
  • we don't have to create smaller components to be able to use the async pipe only once and prevent multiple subscriptions
  • no local data state variables are defined at the level of the component, so its less likely to run into issues caused by mutating local component state
  • we now have a more declarative code: both the component and the template are very declarative, we are simply plugging in together streams of data instead of storing local variables and passing them to the template