Reactive Streams in Angular

$ whoami

  • Engineering manager at Amazon
  • Organizer of Droidcon Boston (and maybe Seattle)
  • Organizer of SwiftFest Boston (and Seattle)
  • Meetups and community enthusiast

@giorgionatili

Agenda

  • Application layers
  • Application model in a nutshell
  • Application state
  • Reactive data streams
  • Demo

Ingredients

  • TypeScript 3.x
  • Angular 7.x
  • rxjs 6.x
  • ngrx 6.x
  • ngrx-forms 3.x

ReactiveX

App Layers

Models

State

Components

├── actions
│   └── user.action.ts
├── app.component.css
├── app.component.html
├── app.component.spec.ts
├── app.component.ts
├── app.module.ts
├── components
│   ├── create
│   └── index
├── models
│   └── User.ts
└── state
    └── user.state.ts

@giorgionatili

Services

Streams

Models

Value Objects

  • Abstraction to represent a complex type
  • Distinguishable only by the state of its properties
  • Comparable on the basis of their collective state
  • In most cases, immutable

@giorgionatili

export interface Person {
  firstName: string;
  lastName: string;
  age: number;
}

Entities

  • Similar to a value object
  • Distinguishable by properties and unique identifier
  • In most cases, immutable

@giorgionatili

export interface User {
  id: string;
  role: Roles;
}

enum Roles {
  Admin,
  RegisteredUser,
  AnonymousUser
}

Aggregates

  • Draw a boundary around one or more entities
  • Describe a self-contained part of the app domain
  • Editable through services (with rules)
  • In most cases, immutable

@giorgionatili

export interface Group {

  id: number;
  name: string;
  users: User[];
  permissions: Roles[];
}

Immutability

App State

Store

  • The store is a global state manager
  • It dispatches actions that state containers listen to
  • It exposes data slices out from the global state

@giorgionatili

import { Store } from '@ngrx/store';
import { AddPerson } from './person.actions';
​
@Component({ ... })
export class AddPersonComponent {
  constructor(private store: Store) {}
​
  addPerson(name: string) {
    this.store.dispatch(new AddPerson(name));
  }
}

Actions

  • Actions can be thought as commands or events
  • Each action contains a type field which is the unique identifier
  • Actions can contain data
  • Actions can be synchronous and asynchronous

@giorgionatili

export class AddPerson {

  static readonly type = '[Person] Add Person';
  constructor(public name: string) {}
}

State

  • States are classes along with decorators to describe metadata and action mappings
  • States are classes that give you a context for modeling, selecting, and handling changes of data.

@giorgionatili

import { State } from '@ngxs/store';
​
@State<string[]>({
  name: 'users',
  defaults: []
})
export class UsersState {}

Selects

  • Selects are a representation of Observable slices of the app state

  • The @Select decorator allows selecting slices of data from the store

  • The slices of data can be acquired passing a state class or a function

@giorgionatili

import { Select } from '@ngxs/store';

@Component({ ... })
export class GroupComponent {

  // Reads the name of the state from the state class
  @Select(GroupState) people$: Observable<string[]>;
​
}

Reducers

  • Reducers specify how the application's state changes in response to actions sent to the store
  • Actions only describe what happened, but don't describe how the application's state changes
  • Reducers are pure function that concur to the manipulation of the app state

@giorgionatili

Data Streams

Reactive forms

  • A model-driven approach to handling inputs
  • Sophisticated validation
  • More readable than template driven forms
  • Encourage decoupling the logic from the presentation

@giorgionatili

@Component({
  selector: 'app-profile-editor',
  template: `
    <form [formGroup]="profileForm">
      <label>
        First Name: <input type="text" formControlName="firstName">
      </label>
      <label>
        Last Name: <input type="text" formControlName="lastName">
      </label>
    </form>
  `,
})
export class ProfileEditorComponent {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
  });
}

Observables

  • Observer subscribes to an Observable
  • Observer reacts to whatever value Observable emits
  • Think of the Observable as a “push” equivalent to Iterable

@giorgionatili

import { from } from 'rxjs';

// Emit array as a sequence of values
const arraySource = from([1, 2, 3, 4, 5]);

const subscribe = arraySource
    .subscribe(val => console.log(val));

Rx Operators

  • ReactiveX provides a wide collection of operators
  • Operators can filter, select, transform, combine, and compose Observables
  • Most operators operate on an Observable and return an Observable

@giorgionatili

https://www.learnrxjs.io/operators/

import { from } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';

const myArrayWithDuplicatesInARow = from([1, 1, 2, 2, 3, 1, 2, 3]);

const distinctSub = myArrayWithDuplicatesInARow
  .pipe(distinctUntilChanged())
  .subscribe(val => console.log('DISTINCT SUB:', val));

// Output: DISTINCT SUB: 1,2,3,1,2,3

Pipe

  • You pull in any operator you need from  'rxjs/operators'
  • You concatenate the operators
  • Each operator is a pure function

@giorgionatili

Demo

Q&A

Useful links

  • https://ngxs.gitbook.io/ngxs/advanced/meta-reducer
  • https://github.com/ngxs/store/issues/139#issuecomment-390673126
  • https://angularfirebase.com/lessons/ngxs-quick-start-angular-state-management/
  • https://ngxs.gitbook.io/ngxs/concepts/select

Thanks!

Keep calm and streakm on...

Reactive Streams in Angular

By Giorgio Natili

Reactive Streams in Angular

Linus Torvalds is not an easy person to deal with, but he did great things and shared interesting thoughts with the community. Once he said "Talk is Cheap, Show Me the Code," and he was right. Join me in this talk for a code walkthrough to define the architecture of an Angular reactive app that consumes data streams by mixing observables, events, pipes and relying on an immutable state.

  • 977