So you've just been handed an Angular project and are trying to figure out how to get up to speed on Angular AND TypeScript AND RxJS. Take a deep breath, it's going to be ok! Here's a quick crash course to key TypeScript and RxJS concepts you need to know to get started ASAP:
TypeScript
Angular is written in TypeScript, and when using it's classes and interfaces you'll need a basic understanding of TypeScript concepts.
Public vs. Private Modifiers
What they are: A way to modify access to members and methods. Unspecified are public by default.
Why you care: Marking members and methods private makes them inaccessible from other classes.
Worth noting: Marking a member as public in a class constructor is a common Angular pattern you'll see that is shorthand for setting that member on the class.
class Person {
constructor(public name: string) {
}
}
//same as
class Person {
public name;
constructor(name: string) {
this.name = name;
}
}
Types
What they are: A way to define what type your units of data should be. Types are boolean, number, string, array, tuple, enum, any, void, null, undefined, never.
Why you care: Assigning types helps us easily avoid buggy code at compile time. If you try to assign a value to a variable that doesn't match it's specified type, your TypeScript won't compile.
Worth noting: TypeScript will attempt to infer Types when a type is not specified, from single values to complex objects.
let name: string;
name = 'Jennifer' //works!
name = 1337 // will error
let arrayOfStrings: string[];
let anotherArrayOfStrings: Array<string>;
arrayOfStrings.push('hi') //works
arrayOfStrings.push(27) //errors
Template Strings
What they are: No, this wasn't a backtick copy-paste error! Template strings are a quick way to embed variables in strings using a backtick.
Why you care: They save you keystrokes!
Worth noting: They can have multiple lines.
let myName = 'Jennifer';
console.log(`Hello, ${myName}`);
let myHTMLTemplate = `<p>Hello, ${myName}</p>`
Interfaces
What they are: A way to define a contract for how an object, function, or class should look like.
Why you care: Not only can interfaces greatly aid development, but there are many useful Angular interfaces and knowing how to implement them will help you write less repeat code.
Worth noting: Classes implementing interfaces MUST implement all required methods and properties described by the interface. Also interfaces are NOT generated from the source code, they are just used at compile time.
interface Person {
firstName: string
lastName: string
age?: number
}
let person1: Person;
person1 = {
firstName: 'Jennifer',
lastName: 'Wadella'
} //works
let person2: Person;
person2 = {
name: 'Jennifer'
} //errors
Functions
What they are: Well, they're functions, but in TypeScript we can give the functions a return type as well as set the types of the parameters.
Why you care: If you pass incorrect types to a function, pass more or less arguments than specified by the function, or try to return a value from a function with the incorrect type, TypeScript won't compile.
Worth noting: Function parameters can be marked as optional using ?
function buildGreeting(name: string, phrase: string): string {
return `${phrase}, ${name}`
}
These concepts will get you started working with TypeScript in an Angular app quickly, but to learn more in depth check out the Bitovi Academy TypeScript Guide!
RxJS
Observables
What they are: A publish/subscribe pattern that allows us to work with values that may change over time, for example, the value of a form input.
Why you care: Many Angular APIs rely on Observables, so getting familiar with them is a must. Not understanding observables can hang up many new Angular developers - for instance knowing that Observables are declarative and won't execute publishing values until you subscribe to them.
Worth noting: Two key parts of Angular you'll interact with often - HTTPclient and Router both rely on Observables and RxJS.
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'my-component',
template: `<p>{{myDisplayValue}}</p>`,
styleUrls: ['./app.component.less']
})
export class MyComponent implements OnInit {
public myFakeObsData: Observable<string>;
public mySubscription;
public myDisplayValue;
constructor() {
}
ngOnInit(): void {
this.myFakeObsData = new Observable(observer => {
setTimeout(() => {
observer.next('hello');
}, 1000);
setTimeout(() => {
observer.next('how are you');
}, 2000);
setTimeout(() => {
observer.complete();
}, 3000);
});
this.mySubscription = this.myFakeObsData.subscribe((val) => {
this.myVal = val;
});
}
}
Subscriptions
What they are: How to get values that observables are emitting.
Why you care: This is how you'll get your data! It should feel familiar if you've used promises/async/await previously.
Worth noting: You must unsubscribe to avoid memory leaks unless you're using the async pipe in your markup.
import { Component, OnInit } from '@angular/core';
import { ObsService } from '../obs-service.service';
import { Observable, Subscription } from 'rxjs';
@Component({
selector: 'my-component',
template: `<p>{{myDisplayValue}}</p>`,
styleUrls: ['./obs1.component.less']
})
export class MyComponent implements OnInit {
$mySubscription: Subscription;
myDisplayValue: number;
constructor(public obsService: ObsService) { }
ngOnInit() {
this.$mySubscription = this.obsService.getObs().subscribe((val) => {
console.log('new value', val);
this.myDisplayValue = val;
});
}
ngOnDestroy() {
if(this.$mySubscription) {
this.$mySubscription.unsubscribe();
}
}
}
Using the async pipe:
import { Component, OnInit } from '@angular/core';
import { ObsService } from '../obs-service.service';
@Component({
selector: 'app-obs-async',
template: `<p>{{myDisplayValue | async}}</p>`,
styleUrls: ['./obs-async.component.less']
})
export class ObsAsyncComponent implements OnInit {
public myDisplayValue;
constructor(public obsService: ObsService) { }
ngOnInit() {
this.myDisplayValue = this.obsService.getObs();
}
}
Operators
What they are: Ways to perform operations on values emitted by observables. Think of them as Lodash for streams.
Why you care: They are helpful to transform and map data.
Worth noting: The tap operator is a way to console log values without creating a new subscription. This is useful for debugging.
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
@Component({
selector: 'app-root',
template: `<p>{{myVal | async}}</p>`,
styleUrls: ['./app.component.less']
})
export class AppComponent implements OnInit {
public myFakeObsData: Observable<string>;
public mySubscription;
public myVal;
constructor() {
}
ngOnInit(): void {
this.myFakeObsData = new Observable(observer => {
setTimeout(() => {
observer.next('hello');
}, 1000);
setTimeout(() => {
observer.next('how are you');
}, 2000);
setTimeout(() => {
observer.next('good I hope');
}, 3000);
setTimeout(() => {
observer.complete();
}, 4000);
});
const addName = map( (value) => value + ', Jennifer' );
const addEnd = map( (value) => value + '!');
let examplePipe1 = this.myFakeObsData.pipe( addName )
.pipe( tap( (value) => console.log("val", value) ) );
let examplePipe2 = examplePipe1.pipe( addEnd );
this.myVal = examplePipe2;
}
}
These concepts will get you started working with RxJS in an Angular app quickly, but to learn more in depth check out the Bitovi Academy RxJS Guide!