AI-generated image from the prompt “create signals out of observables”

Signals are becoming increasingly an alternative to RxJs in Angular applications.

Of course, there are the toSignal and toObservable functions to implement interoperability between Angular Signals and RxJs Observables, but what if we want to get more features than the basics?

For instance, say we’d like to know if an Observable is still loading its initial data or if an error happened. Wouldn’t it be great to be able to do something like this?

@if( myData.loading() ) {
Loading data...
} @else {
{{ myData.data() }}
}
@if (myData.error()) {
Something went wrong!
}

These three methods loading, data, and error, are three signals stored on what I call a SignalState object, which has the following interface:

export interface SignalState {
loading: Signal;
data: Signal;
error: Signal;
}

How to create such a SignalState object? You can use a small library called ngx-signalify. I’m the author of this tiny library, and your feedback is welcome to improve and expand it (Github repo here).

To install: npm install ngx-signalify

Then import and call the signalify function on an Observable, for instance an Observable coming from the HttpClient:

import { signalify } from 'ngx-signalify'; 

http = inject(HttpClient);
myData = signalify(this.http.get(URL));

Like with regular signals, no subscription is needed, and you get the extra loading and error signals to know when your data is available or if an error happens.

You can see a complete example in action here on Stackblitz:

As a recap, you can turn an Observable into a SignalState object using the signalify function.

Then you can use:

.loading() to read the value of a Signal that returns true if the data is still loading, false otherwise.

.error() is a Signal that returns undefined if there’s no error, and the error otherwise.

.data() is a Signal that is undefined at first, then returns the current value of the source Observable.

All of this can fit in a single component easily:

@Component({
selector: 'app-root',
standalone: true,
template: `
@if(comments.loading()) {
Loading comments...
} @else {
@let list = comments.data() ?? [];
We have {{list.length}} comments
@for(comment of list; track comment.id) {
  • {{comment.body}} (by {{comment.email}})

  • }
    }
    @if (comments.error()) {
    Something went wrong!
    }
    `,
    })
    export class App {
    http = inject(HttpClient);
    comments = signalify(this.http.get(URL));
    }

    What do you think? What other features would you like to see in ngx-signalify? Let me know in the comments or open a request on Github.

    My name is Alain Chautard. I am a Google Developer Expert in Angular and a consultant and trainer at Angular Training, where I help web development teams learn and become comfortable with Angular.

    If you need any help with web development, feel free to get in touch!

    If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and my Daily Angular Newsletter to receive helpful daily tips.

    Share.
    Leave A Reply