Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Observables in Angular

Observables are pretty similar to .NET’s Events. The Observable<T> is a stream of data that we can subscribe() to to get the latest values. This functionality comes with the RxJS package.

An Observable<T> might deliver 3 kinds of messages:

  • data of type T
  • error
  • completion - signifies that this Observable will not deliver any more data

Observables can be finite, meaning the stream of data will eventually finish and the observer(s) will be notified about that (HTTP request is an example of such an Observable). Observables might also be infinite and the completion will never occur.

$ Postfix

In Angular, there is an unofficial convention to postfix variables representing Observable<T> with a $ sign. Additionally, often the last value of the observable is stored in a variable called the same as the one holding the Observable<T>, but without the ”$” sign this time.

temperature$: Observable<number>;
temperature: number; // last value of temperature$

Built-in Observables

RxJS comes with a few built-in functions that create observables:

  • interval - works similarly to JS’s setInterval. We set the interval in milliseconds, incoming values are incresing, like in a counter (starting from 0).
  • create - we’re given a function where values emitted by the new observables need to ne “manually” pushed out (example is below).
  • and a few more…

Custom Observables

Here’s how we can create our own Observables. This is a custom interval implementation:

let customInterval = Observable.create(observer => {
let i = 0;
setInterval(() => {
observer.next(i++);
}, 1000);
})
const sub = customInterval.subscribe(value => {
console.log(value);
}
sub.unsubscribe();

Operations

Removing Subscription

Subscriptions stay for as long as the app lives. If we are no longer interested in receiving values, we should explicitly remove the subscription. Otherwise, we’d have a memory leak.

// Create a subscrition
const subscription : Subscription = interval(1000).subscribe(
value => console.log(value));
// Remove the subscription
subscription.unsubscribe();

In Angular, most often we will want to remove the subscription when some component is no longer displayed. In such case, the component should implement OnDestroy, which comes with the ngOnDestroy lifecycle hook. We should remove subscription there.

Router

Angular’s Router allows us to subscribe to various observables (e.g. params). Exceptionally, we don’t need to unsubscribe from them, because Angular will do that for us in the background. Using unsubscribe() does not cause any harm though.

HttpClient has similar characteristics.

Error handling

Subscription can have have an error. For example, when doing an HTTP request. In such a case the subscription “dies” and we do not have to call unsubscribe() on it.

We can also handle errors by providing error handler as a second argument to subscribe().

const sub = someObservable.subsctibe(
value => console.log(value),
error => console.log(error)
);

Without handling errors explicitly, Observable will throw the error to the console.

catchError

We can also use the catchError operator. We can attach it once to some observable, to include some common error handling logic. We can rethrow an error from withing that operator with throwError(...). Then, each subscriber may handle the rethrown error individually.

const observable = someObservable.catchError(e => {
// log the error or whatever..
throwError(e);
})
const sub1 = observable.subscribe(n => {
// do somehting...
}, error =>{
// handle rethrown error...
})
const sub2 = observable.subscribe(n => {
// do something, and hope it will not fail...
})

Like the example above shows, the individual subscribers can still attach their own error handling logic. The “common” handler will always be fired first, and then the error will be propagated to the subscribers.

Completion

Observables can be finite. Here’s how we can complete custom observables:

let customInterval = Observable.crete(observer => {
let i = 0;
setInterval(() => {
observer.next(i++);
if(i >= 5) {
observer.complete();
}
}, 1000);
})

Subscribers can be notified about completion via a third argument to the subscribe() method:

const sub = someObservable.subsctibe(
value => console.log(value),
error => console.log(error),
() => console.log('COMPLETE')
);

We don’t need to unsubscribe() from completed subscriptions.

Error

In cases when subscription throws an error, completion will never occur.

Operators

We might want to somehow change the behaviour of some subscription. For example, we might want to filter out some values, or maybe transform them into some other data. This is where the Operators come in. They allow us to create new Observables based on existing ones.

Here’s an example:

const originalObservable = interval(1000);
const modifiedObservable = originalObservable
.pipe(map(value: number => value + 1));
modifiedObservable.subscribe(val => console.log(val));

We apply operators with the pipe() function. We used the map operator. There are lots more in the rxjs/operators import. For example:

  • filter rejects values that do not conform to the provided predicate;
  • catchError allows to handle error “globally” somehow and throwError (possible a new one) down the chain
  • take - automatically completes the resulting Observable after receiving n items from it. We do not need to unsubscribe() from it, it will be handled automatically. It is useful when we want to get just one latest value of some Observable (like the BehaviorSubject described down below).
  • exhaustMap similar to map, but accepts function that crosses boundaries (more on that in the tip below)

Maps

There’s a nice overview of different kids of maps, like map, mergeMap, switchMap at Medium.

exhaustMap

An Observable<T> could be treated as a functor and a monad.

The map operator is like a Map on a functor. It turns Observable<T> into Observable<U> by accepting a function that turns a regular value T into a regular value U.

The exhaustMap operator is like a Bind on a monad. It turns Observable<T> into Observable<U> by accepting a function that turns a regular value T into an elevated value - Observable<U>.

Chaining

To chain a few operators sequentially, we can call pipe() multiple times, or we can provide more operators as next arguments to pipe().

Subject

Rxjs comes with a special type of Observable - Subject. It is functionally very similar to Angular’s EventEmitter<T>. Reportedly, it’s more performant though and it’s recommended to always use Subject.

@Output

EventEmitter<T> is still needed when we’re dealing with @Outputs! Subjects are usable only when we control both the producer and the consumer sides.

const mySubject = new Subject<number>();
// Emit
mySubject.next(5);
// Subscribe
const sub = mySubject.subscribe(value => {
// do something...
})
//Unsubscribe
sub.unsubscribe();

Operators

We can use operators on Subjects, we can’t do that with EventEmitter.

BehaviorSubject

There’s also another variant of Subject called BehaviorSubject. It comes with an additional feature - the subscriber is always able to read the last value of the Subject, even if it subscribes after that value was published. It needs to be initialized via constructor with some initial value.

Combining Observables

In some cases we might need to use values from multiple observables to create some new value. It is often used in ngRx effects.

RxJs offers a few ways to combine observables. Some of these are operators, and some are functions that allow us to create new Observables, based on the ones we provide.

ngRx selectors

In NgRx, our state often has some initial default values (often null or undefined). These are counted as “emitted values”, meaning that the operators/function below will ues them to produce the output. It might make sense to skip these initial values with some operator like skipWhile(n => !n).

Operators

withLatestFrom

RxJS withLatestFrom operator visualized

It will publish only at points of time when the first observable emits. The first output will come out only after all of the provided observables have emitted at least one value.

combineLatestWith

RxJS combineLatestWith operator visualized

It’s pretty similar to withLatestFrom. The difference is that the output is emitted when any of the input observables emits something, the first observable is not prioritized in any way.

combineLatest

There is also a combineLatest operator, which is pretty similar to combineLatestWith, but it’s deprecated.

There also is a combineLatest function (not the same as the deprecated operator!) that behaves pretty similar to the combineLatestWith operator, and it may be used to create observables from other observables. The difference is that operator fires when the source observable emits, while the function like combineLatest (not an operator) creates an observable, that one can subscribe to, and only then it will produce values.

References

  • Simple Observable Implementation
←  Routing
Forms  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.