RxJS

WebSerial.app - Browser to USB Serial Communication With Svelte

 Published: Feb 5, 2021  (last updated: Feb 12, 2021)~ 200 words~ 1 minutes reading time

After my previous experiments with the Web Serial API, I started experimenting with Svelte .

Within a couple of days I have created https://webserial.app/ - the Web Serial Controller app.

The interface might seem familiar - it’s based on XP.css - a Windows XP CSS theme. It was inspired by some of the Serial hardware software used in the 00’s.

The interface of Web Serial Controller

The application is fully open source and features:

  • Fully connected state - use the screens or keyboard shortcuts to connect and disconnect from devices with shared state
  • Filter devices by vendor ID with a fully searchable list of all hardware vendors
  • A draggable interface XP-like interface
  • Options storage in localStorage
  • Send text messages to any connected device

Web Serial API with RxJS - Two-Way Reactive Communication between Browser and Serial Hardware

 Published: Jan 30, 2021  (last updated: Feb 12, 2021)~ 1100 words~ 6 minutes reading time

Version 89 of Chrome and Edge browsers have released the Web Serial API unflagged which means as user it’s now available for general use rather than being locked behind experimental flags (if you’re on an earlier version you can enable Experimental Web Platform features in chrome://flags)

The API allows for communication between the browser and supported serial hardware such as Arduino or RaspberryPi over USB Serial connection - the device registers as available to the browser and a port can be opened.

If you don’t have any hardware to connect to, you can easily test it using a Bluetooth Serial connection - provided your computer has a Bluetooth module you can connect your mobile device to it and use the appropriate software.

Connecting Browser to Hardware

To request access to a device, a call needs to be made to the newly available function navigator.serial.requestPort:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const startButton = document.getElementById("start");

startButton.addEventListener("click", async event => {
  try {
    const port = await navigator.serial.requestPort();
    // We can now access the serial device by opening it
    // e.g. await port.open({baudRate: 9600})
  } catch (e) {
    // The prompt has been dismissed without selecting a device.
  }
});

This function is part of a set that must be called from a specific set of user interactions such as touch or click events - in the demo after a user gesture such as a button click - you cannot just call requestPort from your code without some kind of user interaction as this will cause a security violation. You also must call it from a location that does not have policy set up to disable this (you can see this in the demo above - if you try run it in the editor it won’t work due to the <iframe> not having the correct policy).

You may also need to install the w3c-web-serial types in your project to make sure you have the available types on the navigator object and global types such as SerialPort.

To get a port, call navigator.serial.requestPort inside the handler - it will return a Promise that contains the port object - you can also wrap it in a try/catch to handle when the user cancels device selection.

The port object once created must be called with the open method - the only required property of the options is the baudRate which is the maximum bits-per-second transferred but there are other options based on the requirements of the device.

Once opened the port can return a ReadableStream and WritableStream which allows data to be passed to and from the device.

Our RxJS Operator

To turn this into an RxJS operator we’ll consume the port and set up the functionality to both read and write to the serial bus. You can read the full source code to see how the final Observable was created, but we’ll cover the important sections below.

Reading from the Serial Bus

Once connected, the serial device can start sending data to us - as it’s a ReadableStream the result will be a UInt8Array.

Here we’ll set up an iterable reader for our stream - while the result is not done and the port is still readable, we’ll continue to read the source and emit it to the subscriber of the Observable. If the reader has completed, or the port has been closed we’ll end this iteration.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
await port.open({baudRate: 9600});

const process = async (
  result: ReadableStreamReadResult<Uint8Array>
): Promise<ReadableStreamReadResult<Uint8Array>> => {
  subscriber.next(result.value);
  return !result.done || !port.readable
    ? reader.read().then(process)
    : Promise.resolve(result);
};

if (port.readable) {
  reader = port.readable.getReader();
  reader.read().then(process);
}

As the output of our Observable is a Uint8Array. Depending on your needs you can decode this to the format you need, but in most cases it will be text content - here we can use a TextDecoder to get the value:

1
2
3
4
5
6
7
8
const decoder = new TextDecoder("utf-8");

fromWebSerial(port).pipe(
  tap(value => {
    // Value is a UInt8Array, we can append to a element by decoding it
    outputEl.innerHTML = decoder.decode(value)
  })
).subscribe()

Writing to the Serial Bus

The API also allows for writing data to the device, here we can use another Observable that emits a string and provide it to our function as a source, then we can hook it up to the ports WritableStream.

Instead of directly writing, we will create a TextEncoderStream - this allows us to create a new internal writer that we have more control over - it contains both a reader and writer we use this to connect our sources.

The reader from our encoder will be piped to the ports WritableStream, and the writer passed to toWritableStream which connects the Observable to the writer:

1
2
3
4
5
6
7
8
if (writerSource && port.writable) {
  const encoder = new TextEncoderStream();
  writerEnd = encoder.readable.pipeTo(port.writable);
  const outputStream = encoder.writable;

  writer = outputStream.getWriter();
  writerSource.pipe(toWritableStream(writer, signal)).subscribe();
}

Now we can pass the Observable and use it to emit our values:

1
2
3
4
5
const emitter$ = new Subject<string>();

fromWebSerial(port, emitter$.asObservable()).subscribe();

emitter$.next('Hello There!');

Creating a Serial Chat App

Now we can read from, and write to, our hardware device the possibilities are endless with what we can do - provided the hardware supports it.

For this tutorial I build a very basic chat app - using the Bluetooth Serial applications mentioned above you can use it to send and receive text data between devices.

A screenshot of a web serial mobile app A screenshot of a web serial browser app

In the example code I’ve set up a button to enable our port request - you should see a popup with a list of devices available for you to use. After connecting a basic chat interface will show up - type in some text and check out your device software - you should see the same message there, and you can then send a message back to the browser.

Hopefully you’ve found this tutorial useful and if you do build something with this I’d love to hear about it!

A collection of pre-built operators and Observables for your projects

The RxJS Logo, a Ninja jumping over a moon

RxJS Ninja - is a collection of over 130 operators for working with various types of data (such as arrays , numbers ) and streams allowing for modifying, filtering and querying the data.

Still in active development, you might find useful operators that provide clearer intent for your RxJS code.

You can check out the source code on GitHub .

Create your own Dark Mode Detection Observable using RxJS and Media Queries

 Published: Jan 27, 2021  (last updated: Feb 12, 2021)~ 1100 words~ 5 minutes reading time

Demo Link

One of the more recent features available in browsers is ability to do CSS Media Queries based on user theme & accessibility settings in the operating system - for example using @media (prefers-color-scheme: dark) (see prefers-color-scheme ) you can check if the users OS theme is currently in Dark Mode and use this to set a websites theme accordingly.

The query is also available in JavaScript using the window.matchMedia function - that returns a MediaListQuery that will allow us to do two things:

  • The current value of the users setting via the matches boolean property
  • Any future values by listening to its changes event and attaching an event lister function to it

Combining these, it’s the perfect candidate to turn into a fully reactive dark mode switcher using RxJS and Observables that will give us the users current setting. If you’re not familiar with Observables, they are a type of stream that emits values over time - consumers can subscribe to these Observables to get their values - this means we can use them to get values from long running functions or event emitters.

In the full demo you’ll find an example page with light and dark mode set from your own OS settings. To see the full working example you need to change the setting (e.g. in OSX Dark Mode is under “General” settings) - also provided are a user toggle button, and a button to turn off and on the media query listener. The Observable in this example supports more than one prefers- type of query but in the tutorial below we’ll build a much simpler isDarkMode Observable than the one provided in the demo, but the concept is the same.

Creating a Dark Mode Observable

For our code we first need to create our Observable factory - this is our function that allows us to pass any required parameters for the implementation and returns an Observable which can then be subscribed to.

The Observable constructor takes a function - a callback any time there is a new subscription - this is where the implementation will live.

As soon as the subscription opens, we first check to see if window.matchMedia is available - it should be available in all modern browsers but is not available in environments like node (yay unit testing!) - so here we can throw an error.

The factory also accepts an optional AbortSignal , an object that contains an onabort callback - the parent of the signal is a AbortController and using this we can externally signal our Observable to close all subscriptions and remove all event listeners.

The return value of the constructor is another function - the teardown logic - this is called when an RxJS subscription is ended, such as using takeUntil or take(1) - here we also ensure that all subscriptions and event listeners are closed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import { Observable } from 'rxjs';

export function isDarkMode(signal?: AbortSignal): Observable<boolean> {
  return new Observable<boolean>(subscriber => {

    if (!window.matchMedia) {
      subscriber.error(new Error('No windows Media Match available'));
    }

    if (signal) {
      signal.onabort = () => {
        !subscriber.closed && subscriber.complete()
      }
    }

    return () => {
      !subscriber.closed && subscriber.complete()
    }
  });
}

Adding the Media Query

The main implementation of our Observable is to create our MediaListQuery and use it to emit values to any subscribers. On creation, contain a matches value of true or false which can be immediately be passed to subscriber.next.

We also need to bind a listener using to the change event of the query. As we also need to remove this later create an internal private function for the event handler - this will also call subscriber.next each time there is a detected change.

Also casting the event to a MediaQueryListEvent ensures TypeScript recognises it has the matches property which contains our value.

1
2
3
4
5
6
7
function emitValue(event: Event) {
  subscriber.next((event as MediaQueryListEvent).matches);
}

const mediaListQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaListQuery.addEventListener('change', emitValue);
subscriber.next(mediaListQuery.matches);

Cleaning up handlers and subscriptions

Already we can start to use the new Observable, but we also need to make sure that we:

  • End any subscriptions to the Observable when either the AbortSignal fires or RxJS unsubscribes from it
  • Remove any event listeners in the DOM for the change event

With a slight bit of refactoring we have our final Observable factory below - in both the signal.onabort and the Observable teardown logic we remove the event listener - the API for this requires you pass the function implementation from our private function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Observable } from 'rxjs';

export function isDarkMode(signal?: AbortSignal): Observable<boolean> {
  return new Observable<boolean>(subscriber => {

    if (!window.matchMedia) {
      subscriber.error(new Error('No windows Media Match available'));
    }

    function emitValue(event: Event) {
      subscriber.next((event as MediaQueryListEvent).matches);
    }

    const mediaListQuery = window.matchMedia('(prefers-color-scheme: dark)');

    if (signal) {
      signal.onabort = () => {
        mediaListQuery.removeEventListener('change', emitValue)
        !subscriber.closed && subscriber.complete()
      }
    }

    mediaListQuery.addEventListener('change', emitValue);
    subscriber.next(mediaListQuery.matches);

    return () => {
      mediaListQuery.removeEventListener('change', emitValue);
      !subscriber.closed && subscriber.complete()
    }
  })
}

Finishing up

Now we have a fully working reactive Observable for a Dark Mode media query, this can be used in any application or website to check the users theme setting. The demo provides some more example of how to do this in full.

1
2
3
4
5
6
isDarkMode().pipe(
  tap(value => {
    body.classList.removeClass(value ? 'light' : 'dark');
    body.classList.addClass(value ? 'dark' : 'light');
  })
).subscribe()

This tutorial is just one small example of the kind of things that can be done with RxJS - any API that can emit values over time can be turned into Observables.

A collection of pre-built operators and Observables for your projects

The RxJS Logo, a Ninja jumping over a moon

RxJS Ninja - is a collection of over 130 operators for working with various types of data (such as arrays , numbers ) and streams allowing for modifying, filtering and querying the data.

Still in active development, you might find useful operators that provide clearer intent for your RxJS code.

You can check out the source code on GitHub .

Creating Custom RxJS Operators

 Published: Jan 25, 2021  (last updated: Feb 12, 2021)~ 1500 words~ 7 minutes reading time

RxJS is a popular library available for TypeScript and JavaScript.

It provides APIs for the creation of applications and libraries using asynchronous streams of data and reactive methods. It’s one of the foundation libraries of Angular .

Included in it are over 100 operators - functions that take an Observable stream of data and return values for use in chains of operators.

Many of the operators are low level, and combining them through the pipe method they create a powerful way to work with data.

Creating custom operators for a domain

The good news is it’s also very easy to create new higher-level operators for our domain code - these can be used where you find duplicate or complicated operations.

Creating operators we can also ensure well-tested code using marble testing and they can be shared among your team to make your code more readable and stable.

There are two types of operators that can be created - a MonoTypeOperatorFunction and OperatorFunction and all operators must do two things:

  • Return a function which accepts as its parameter a source from the previous Observable value in the stream
  • Return a value of the same type for MonoTypeOperatorFunction or different type for an OperatorFunction by using the source value with pipe

Below we’ll have an example of each, but first, to support creating the operators we need some code to simplify:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { from } from 'rxjs';
import { map, tap } from 'rxjs/operators';

// Create a cold source that will emit each number
const source$ = from([1, 2, 3, 4, 5]);

// Create a cold source that multiplies each number by `5`
const multiplyByFive$ = source$.pipe(map(value => value * 5));
// Create a cold source that multiplies each number by `10`
const multiplyByTen$ = source$.pipe(map(value => value * 10));

// Subscribe to the sources and console.log the output
multiplyByFive$.pipe(tap(console.log)).subscribe();
// Output: `5, 10, 15, 20, 25`

multiplyByTen$.pipe(tap(console.log)).subscribe();
// Output: `10, 20, 30, 40, 50`

Creating MonoTypeOperatorFunction for single types

As the name suggests a MonoTypeOperatorFunction is a function that works with a single type of data - the input and output value must be of the same type.

Looking at our code we can identify two multiplication operations in our code that are the same. To turn this into an operator the function will look like this:

1
2
3
4
5
6
import { MonoTypeOperatorFunction } from 'rxjs';
import { map } from 'rxjs/operators';

export function multiply(factor: number): MonoTypeOperatorFunction<number> {
  return (source) => source.pipe(map(value => value * factor))
}

Here, we are returning an arrow function that takes the previous source - which must be an Observable<number>. The source is piped to map which allows the source value to be converted to a new value, in our case we multiply by the factor

TypeScript understands that the output must also be a number - and if you try to return another value type it will throw a compile error.

Writing a marble test

Marble testing is a way to write tests for RxJS operators that deal with data over time - data is not static due to its asynchronous nature and cannot always be guaranteed in a specific order. Luckily the test for this operator is simple.

My personal preference has been to write these test in Jest using rxjs-marbles and jest-marbles , but there are other libraries to write these test available.

Using marbles, we can set up a mock source that will emit 5 numbers at the specified frames.

The test result contains two things:

  • A subscriptions string which is used to check that the operator handle subscription ending properly using toHaveSubscriptions
  • An output Observable that will contain the results of the operator and compared against the expectations using toBeObservable

In this test, we’ll pass a source of numbers and multiply by 10

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { marbles } from "rxjs-marbles/jest";
import { map } from "rxjs/operators";
import { multiply } from './multiply'

describe("multiply", () => {
  it("should multiply by 10", marbles(m => {
    const input = m.hot('-a-b-c-d-e-|', {a: 2, b: 3, c: 4, d: 5, e: 6});
    const subs = '^----------!';
    const expected = m.cold('-a-b-c-d-e-|', {a: 20, b: 30, c: 40, d: 50, e: 60});
    m.expect(input.pipe(mul(10))).toBeObservable(expected);
    m.expect(input).toHaveSubscriptions(subs);
  }));
});

Update Code

Now the operator is created it can be used in the existing code from above - ideally the operator should be part of a shared library of code:

1
2
3
4
5
6
7
import { from } from 'rxjs';
import { multiply } from '@myorg/rxjs-library'

const source$ = from([1, 2, 3, 4, 5]);

const multiplyByFive$ = source$.pipe(multiply(5));
const multiplyByTen$ = source$.pipe(multiply(10));

Already much more readable! Our code explains our intent, but we haven’t really reduced the duplication of our sources.

Changing the API with OperatorFunction

In our domain, we know we always want more than one value from a source and using the OperatorFunction we can use that to reduce our duplicate code even more.

This would introduce an API change, but with proper tests, we should be able to migrate our code easily.

For our source value, it is still a single number value, but in the API we’ve changed:

  • The input factor can be a single value or an array of values
  • The return value is now an array of values, regardless of the input.

Instead of forcing the users to check the type of response, this single API can be well documented and expected when we use it in our code:

1
2
3
4
5
6
import { OperatorFunction } from 'rxjs';
import { map } from 'rxjs/operators';

export function multiply(factor: number | number[]): OperatorFunction<number, number[]> {
  return source => source.pipe(map(value => (Array.isArray(factor) ? factor : [factor]).map(f => value * f)))
}

Updating the tests

First, we need to update the existing test - here we only have to change the values in our expected Observable - we now expect an array of numbers regardless of the input - but with a single value our array length will be 1

1
2
3
4
5
6
7
it("should multiply by 10", marbles(m => {
  const input = m.hot('-a-b-c-d-e-|', {a: 2, b: 3, c: 4, d: 5, e: 6});
  const subs = '^----------!';
  const expected = m.cold('-a-b-c-d-e-|', {a: [20], b: [30], c: [40], d: [50], e: [60]});
  m.expect(input.pipe(mul(10))).toBeObservable(expected);
  m.expect(input).toHaveSubscriptions(subs);
}));

To ensure full coverage, we should also test for the case where were have an array input for the multiplication factor:

1
2
3
4
5
6
7
it("should multiply by by 5 and 10", marbles(m => {
  const input = m.hot('-a-b-c-d-e-|', {a: 2, b: 3, c: 4, d: 5, e: 6});
  const subs = '^----------!';
  const expected = m.cold('-a-b-c-d-e-|', {a: [10, 20], b: [15, 30], c: [20, 40], d: [25, 50], e: [30, 60]});
  m.expect(input.pipe(mul([5, 10]))).toBeObservable(expected);
  m.expect(input).toHaveSubscriptions(subs);
}));

Update Code

We can now update the code further - here we can now remove the two additional cold Observables and create a single one using our new multiply operator, passing it an array containing out factors:

1
2
3
4
5
6
import { from } from 'rxjs';
import { multiply } from '@myorg/rxjs-library'

const source$ = from([1, 2, 3, 4, 5]);

const multiplyValues$ = source$.pipe(multiply([5, 10]));

Now we can subscribe to the multiplyValues$ source and get both our new result which contains the multiplication of both numbers

1
2
multiplyValues$.pipe(tap(console.log)).subscribe();
// Output: `[5, 10], [10, 20], [15, 30], [20, 40], [25, 50]`

Next Steps

You can see a working version of this operator on StackBlitz by opening the console to see the result.

This operator is just a taste of what’s possible with RxJS - diving into the API you’ll find many more operators to help work with data in other synchronous and asynchronous operations.

A collection of pre-built operators for your projects

The RxJS Logo, a Ninja jumping over a moon

Now for a shameless plug - my own library - RxJS Ninja - is a collection of over 130 operators for working with various types of data (such as arrays or numbers ) and streams allowing for modifying, filtering and querying the data.

Still in active development, you might find useful operators that provide clearer intent for your RxJS code.

You can check out the source code on GitHub . There you can also find a starter project for creating your own TypeScript libraries like this.

RxJS Ninja Updates - New operators for math and working with streams

 Published: Jan 14, 2021  (last updated: Jan 14, 2021)~ 300 words~ 2 minutes reading time

Since the last update on RxJS Ninja there have been a few new operators added, below are some details and links to StackBlitz demos showing them in action.

Numbers and Math operators

In @rxjs-ninja/rxjs-number the missing toFixed operator has been added, alongside a new custom toHex operator and the corresponding parseHex one allowing hex numbers to be worked with ( such as converting colours)

There are also new operators for some basic math - add , sub , div , mul , mod and pow all allowing you to modify source numbers, all accept a number, or an Observable number source.

Working with Browser Streams

Some new operators have been added to @rxjs-ninja/rxjs-utility that allow interoperability between RxJS and the StreamsAPI . These APIs are not in all browsers but there is an available polyfill .

fromReadableStream

This operator accepts a ReadableStream and provides the emitted values as an Observable, allowing you to use RxJS operators to work with the data.

  • Demo - Using fetch body with fromReadableStream to show partial images
  • Demo - A basic infinite number stream that ticks per second

toWritableStream

This operator accepts a WritableStream . The operator emits the source value and also writes to the stream. The operator takes care of closing the writer when the Observable subscription is closed.

  • Demo - Streams an Observable interval value into a WritableStream with optional ability to stop the writer without ending the Observable subscription.

fromFetchWithProgress

This operator uses fetch to do a HTTP request, instead of the body response it emits either a number which is the current progress, or a Uint8Array containing the final response from the body.

  • Demo Fetches an image and shows a progress bar with the current percentage and once complete shows the image.

RxJS Primitives is now RxJS Ninja

 Published: Nov 23, 2020  (last updated: Nov 23, 2020)~ 100 words~ 1 minutes reading time

RxJS Ninja Logo is a Ninja jumping over a crescent moon

Today I’ve re-released RxJS Primitives as RxJS Ninja . The new libraries have been published as the same last versions under their old name, so it’s easy to migrate to the new version - all now published under @rxjs-ninja instead of @tinynodes (a deprecation notice has also been left the old packages).

New RxJS Primitives release, new operators + Typescript 4

 Published: Nov 18, 2020  (last updated: Nov 18, 2020)~ 900 words~ 5 minutes reading time

This week I released new versions of my RxJS libraries in rxjs-primitives . Since it’s released I’ve added a few new utility operators. Some of these have been out for a while since I originally wrote about the release, so I’ve highlighted them here as they may be useful to some developers.

You can check out the full docs here .

Typescript 4

Upgrading to Typescript 4 has allowed the removal of polymorphic functions in place of Vardic Tuple Types and is why there is a major bump on all packages.

This can be seen in the old and new concat operator in the rxjs-string package.

Most of the tests have also been converted to rxjs-marbles allowing for more robust Observable testing (if you are working with RxJS I highly recommend checking it out, it integrates well with runners like Jest).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
it(
    'should return string value of string ending with passed character',
    marbles((m) => {
      const input = m.hot('-a-b-c-|', { a: 'test', b: 'testing', c: 'gone' });
      const subs = '^------!';
      const expected = m.cold('---y---|', { y: 'testing' });
      m.expect(input.pipe(filterEndsWith('g'))).toBeObservable(expected);
      m.expect(input).toHaveSubscriptions(subs);
    }),
  );

rxjs-array

npm install @tinynodes/rxjs-array

In the array module there are some operators to use with finding the difference or intersection between a source and a passed array, for example:

1
2
3
4
5
6
7
of(['a', 'b', 'd'])
 .pipe(difference(['a', 'c']))
 .subscribe(console.log) // ['b', 'd']

of(['a', 'b', 'd'])
 .pipe(intersects(['a', 'c']))
 .subscribe(console.log) // ['a']

These methods accept an array, or an Observable<Array> of items to compare against.

The module also included a binarySearch operator which returns a custom BinarySearchResult tuple.

rxjs-boolean

npm install @tinynodes/rxjs-boolean

A new Luhn algorithm operator luhnCheck is provided that does validation on numbers such as credit cards, ID cards and other value schemes that use the check.

1
2
3
fromString('4485275742308327')
    .pipe(luhnCheck())
    .subscribe(console.log) // true, this is a valid credit card

rxjs-number

npm install @tinynodes/rxjs-number

inRange / outOfRange and filterInRange / filterOutOfRange both all two numbers, the filter methods return the value from the source observable within the range of those values, while the other methods return a boolean value if in range. An optional third value will include/exclude the range value based on the method

1
2
3
4
5
6
7
8
fromNumber([-1, 0, 1, 2, 10, 11])
 .pipe(filterInRange(0, 10))
 .subscribe(console.log) // [0, 1, 2, 10]

// Passing true as the third parameter, the range numbers will also be excluded
fromNumber([-1, 0, 1, 2, 10, 11])
 .pipe(filterInRange(0, 10, true))
 .subscribe(console.log) // [1, 2]

rxjs-string

npm install @tinynodes/rxjs-string

New operators such as titleize , repeat and match add new utility features for strings. Where they can they also support localisation:

1
2
3
4
5
6
7
fromString('Mary had a little lamb')
 .pipe(titleize())
 .subscribe(console.log) // 'Mary Had A Little Lamb'

fromString('Mary had ä little lamb')
 .pipe(titleize('de-DE'))
 .subscribe(console.log) // 'Mary Had Ä Little Lamb'

rxjs-utility

npm install @tinynodes/rxjs-utility

The utility module contains some specialised tap operators such as tapIf, startWithTap and tapOnSubscribe. These provide a way to do side effects. With startWithTap it can be used with Angular to do a form touch, also tapOnSubscribe will fire when there is a subscription to the Observable:

1
2
3
4
5
6
7
8
9
// Only touch on first value change
form.valueChange.pipe(
 startWithTap(() => this.onTouch())
).subscribe()

// Fire when a component subscribes to the service bus
this.serviceBus.pipe(
  tapOnSubscribe((name: string) => console.log(`New Subscription to ${name}`))
).subscribe()

The tapIf will only fire if a passed method result is truthy:

1
2
3
fromNumber([1, 2, 3, 4, 5, 6]).pipe(
  tapIf((val) => val % 2 === 0), (val) => console.log(val)
).subscribe() // 2, 4, 6

The last operator is mapIfSource which might be a bit of a weird one but I hope might become useful.

The operator takes the value from the source and passes to a predicate method, and depending on the result will map the result of a passed method. A simple example would be:

1
2
3
4
5
6
7
fromNumber([1, 2, 3, 4, 5, 6]).pipe(
  mapIfSource(
    (value) => val % 2 === 0,
    (value) => val * 10,
    (value) => val * 20
  )
).subscribe() // 20, 20, 60 40, 100, 60

Here, if the result of the predicate is true multiply by 10, otherwise by 20. The method is typed to allow different return values based on the result (so you will have to handle the type later). For example we could even turn it into a FizzBuzz operator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
export function fizzbuzz(): OperatorFunction<number, string | number> {
  return (source: Observable<number>) =>
    source.pipe(
      mapIfSource<number, string, number>(
        (value) => value % 15 == 0 || value % 3 == 0 || value % 5 == 0,
        (value) => (value % 15 == 0 ? `FizzBuzz` : value % 3 === 0 ? 'Fizz' : 'Buzz'),
        (value) => value
     )
  );
}

// And now we use it in our code
fromNumber([1, 3, 5, 15, 16]).pipe(
  fizzbuzz(),
).subscribe() // 1, 'Fizz', 'Buzz', 'FizzBuzz', 16

Hopefully you’ll find these operators useful and feel free to leave feedback and suggestions.

RxJS Primitives - Operators for mutating and filtering primitives

 Published: Apr 23, 2020  (last updated: Apr 23, 2020)~ 400 words~ 2 minutes reading time

Today I’ve published a new set of libraries to NPM - RxJS Primitives .

These are based on some operators I’ve collected over the last year, and some additional ones I’ve started adding. Most are based around ECMASCript objects such as String, Number and Boolean but also includes some useful utility operators.

Over the coming weeks I’ll add more operators, both based on ECMAScript methods and custom functions that I have found useful.

The following modules are on NPM:

rxjs-string

@tinynodes/rxjs-string operators that are built around the String object in ECMAScript, for example with toUpperCase:

1
2
3
from(['hello', 'world']).pipe(
  toUpperCase()
).subscribe(value => console.log(value)) // ['HELLO', 'WORLD']

There are also some boolean value operators such as endsWith and extraction operators like charAt, with plans to add more useful utilities. For example endWith returns a boolean value, but I also want to include an endsWith that returns the original value instead (like filter).

rxjs-number

@tinynodes/rxjs-number operators that are built around the Number object in ECMAScript, for example parseFloat/parseInt and isNaN.

1
from(['1', '1.2', '3.14']).pipe(parseInt()).subscribe(value => console.log(value)) // [1, 2, 3]

This also includes toString which uses Number.prototype.toLocaleString supports formatting such as currency.

rxjs-boolean

@tinynodes/rxjs-boolean operators that are built around the Boolean object in ECMAScript, and are designed to help with filtering content from observables. Currently, there are two operators firstTruthy and filterTruthy.

In both cases these return the underlying value only if it’s a truthy value in JavaScript, in the case of firstTruthy it only returns the first value, while filterTruthy returns all truthy values.

rxjs-utility

@tinynodes/rxjs-utility is a custom module that provides some additional operators that don’t fit into the other packages but still have some usefulness.

Currently, there are two operators:

  • startWithTap - Will fire a callback method only on the first emission from an Observable
1
2
3
form.valueChanges.pipe(
  startWithTap(() => form.touch()),
).subscribe()
  • debounceWithQuery - Debounces an input such as a text input and passes it to a method that returns a value from a query (such as a search)
1
2
3
searchField.valueChange.pipe(
  debounceWithQuery(1000, (search) => http.get(`/search?query=${search}`))
).subscribe()