Angular is an object-oriented framework.
Even if there are a lot of things imperatively implemented, some services, and therefore also some third party libs, are reactive.
This is great because it provides both approaches in one framework, which is at the moment a more or less unique thing.
As reactive programming is hard for an imperative thinking mind, many people try to avoid it.
This article will help us to understand how to avoid it and also, we will learn where it makes sense to use observables.
As good example where reactive programming becomes easy
đŠ @rx-angular/state should be named.
It's imperative functions set
and get
allow programmers to code in there used style while still get better performance.
Table of Content
- TL;DR
- Minimal Information about RxJS
- Comparing Basic Usecases
- Patterns to avoid observables
- Composing asynchronous processes
- My 2 cents
- Summary
- Glossary
TL;DR
If you DON'T want to use a reactive approach in your component you
should take the observable you want to get rid of, as soon as possible and do the following things:
- subscribe to a stream and assign incoming values to a component property
- if necessary, unsubscribe the stream as soon as the component gets destroyed
Minimal Information about RxJS
Observables are a unified API for pull- and push-based collections,
that can be composed in a functional way
As this sentence is maybe not trivial to understand let me split it into two pieces, unified API and functional composition.
I'll give a bit more information to both of them and compare them with an imperative approach.
Unified API:
Think about all the different APIs in the browser for asynchronous operations:
-
setInterval
andclearInterval
-
addEventListener
andremoveEventListener
-
new Promise
and [no dispose logic implemented] -
requestAnimationFrame
andcancelAnimationFrame
-
async
andawait
These are just some of them and we can already see they are all implemented differently.
Some of them, i.e. promises, are not even disposable at all.
RxJS wraps all of them and provides the following API:
-
subscribe
andunsubscribe
This is meant by "a unified API".
Functional Composition:
To give an example, let's combine the items of two arrays into a new one.
The imperative approach looks like that:
const arr1 = [1,2,3], arr2 = [4,5,6];
let arr3 = [];
for (let i of arr1) {
arr3.push(i);
}
for (let i of arr2) {
arr3.push(i);
}
We mutate the arr3
and push all items from arr1
and arr2
into arr3
by using the for ... of
statement.
The functional approach looks like that:
const arr1 = [1,2,3], arr2 = [4,5,6];
const arr3 = arr1.concat(arr2);
Here we create a new instance on an array that is a result of the concat
array first-class function.
This is meant by "functional composition".
In the following article, we will learn
how to work with all the different APIs of the browser instead of the unified API of RxJS.
We will also see how to mutate state and leverage imperative programming instead of functional composition.
To elaborate with some more practical things we start with a part of Angular that provides reactivity and try to avoid it.
Comparing Basic Usecases
In this section, we will get a good overview of some of the scenarios we get in touch with reactive programming in Angular.
We will take a look at:
- Reactive services provided by Angular
- Cold and Hot Observables
- Subscription handling
And see the reactive and imperative approach in comparison.
Retrieving values from single-shot observables
Let's solve a very primitive example first.
Retrieving data over HTTP and rendering it.
We start with the reactive approach and then try to convert it into an imperative approach.
Leveraging Reactive Programming (đź demo)
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
@Component({
selector: 'example1-rx',
template: `
<h2>Example1 - Leverage Reactive Programming</h2>
Http result: {{result | async}}
`
})
export class Example1RxComponent {
result = this.http.get('https://api.github.com/users/ReactiveX')
.pipe(map((user: any) => user.login));
constructor(private http: HttpClient) {
}
}
The following things happen here:
- subscribing to
http.get
by using theasync
pipe triggers:- an HTTP
get
request fires - we retrieve the result in the pipe and render it
- an HTTP
On the next change detection run, we will see the latest emitted value in the view.
As observables from HttpClient
are single-shot observables (like promises they complete after the first emission) we don't need to care about subscription handling here.
Avoid Reactive Programming (đź demo)
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'example1-im',
template: `
<h2>Example1 - Avoid Reactive Programming</h2>
Http result: {{result}}
`
})
export class Example1ImComponent {
result;
constructor(private http: HttpClient) {
this.result = this.http.get('https://api.github.com/users/ReactiveX')
.subscribe((user: any) => this.result = user.login);
}
}
The following things happen here:
- subscribing to
http.get
in the constructor triggers:- an HTTP
get
request fires - we retrieve the result in subscribe function
- an HTTP
On the next change detection run, we will see the result in the view.
As observables from HttpClient
are single-shot observables we don't need to care about subscription handling.
Retrieving values from on-going observables provided by an Angular service
Next, let's use an on-going observable provided by Angular service, the ActivatedRoute
service.
Let's retrieve the route params, plucking out a single key and displaying its value in the view.
Again we start with the reactive approach first.
Leveraging Reactive Programming (đź demo)
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { map } from 'rxjs/operators';
@Component({
selector: 'example2-rx',
template: `
<h2>Example2 - Leverage Reactive Programming</h2>
URL param: {{page | async}}
`
})
export class Example2RxComponent {
page = this.route.params
.pipe(map((params: any) => params.page));
constructor(private route: ActivatedRoute) {
}
}
The following things happen here:
- retrieving the new route params by using the
async
- deriving the values from the
page
param fromparams
with a transformation operation using themap
operator - by using the
async
pipe we:- subscribe to the observable on
AfterContentChecked
- applying the internal value to the next pipe return value
- subscribe to the observable on
On the next change detection run, we will see the latest emitted value in the view.
If the component gets destroyed,
the subscription that got set up in the async
pipe after the first run of AfterContentChecked
gets destroyed on the pipes ngOnDestroy
đŸ hook.
There is no manual subscription handling necessary.
Avoiding Reactive Programming (đź demo)
import { Component} from '@angular/core';
import { ActivatedRoute} from '@angular/router';
@Component({
selector: 'example2-im',
template: `
<h2>Example2 - Avoid Reactive Programming</h2>
URL param: {{page}}
`
})
export class Example2ImComponent {
page;
constructor(private route: ActivatedRoute) {
this.route.params
.subscribe(params => this.page = params.page)
}
}
The following things happen here:
- retrieving the new route params by subscribing in the constructor
- deriving the values from the
page
param fromparams
object directly
On the next change detection run, we will see the latest emitted value in the view.
Even if the params
observables from ActivatedRoute
are on-going we don't care about subscription handling here.
Angular internally manages the Observable and it gets closed on ngOnDestroy
of the ActivatedRoute
.
Retrieving values from on-going observables provided by third-party libs
In this section, we take a look at a scenario not managed by the framework.
For this example, I will use the @ngrx/store
library and it's Store
service.
Retrieving state from the store and display its value in the view.
Leveraging Reactive Programming (đź demo)
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
@Component({
selector: 'example3-rx',
template: `
<h2>Example3 - Leverage Reactive Programming</h2>
Store value {{page | async}}
`
})
export class Example3RxComponent {
page = this.store.select(s => s.page);
constructor(private store: Store<any>) {
}
}
The following things happen here:
- retrieving the new state by using the
async
pipe - deriving the values from the
page
param fromthis.store
by using theselect
method - by using the
async
pipe we:- subscribe to the observable on
AfterContentChecked
- apply the internal value to the next pipe return value
- subscribe to the observable on
On the next change detection run, we will see the latest emitted value in the view.
If the component gets destroyed Angular manages the subscription over the async
pipe.
Avoiding Reactive Programming (đź demo)
import { Component, OnDestroy } from '@angular/core';
import { Store } from '@ngrx/store';
@Component({
selector: 'example3-im',
template: `
<h2>Example3 - Avoid Reactive Programming</h2>
Store value {{page}}
`
})
export class Example3ImComponent implements OnDestroy {
subscription;
page;
constructor(private store: Store<any>) {
this.subscription = this.store.select(s => s.page)
.subscribe(page => this.page = page);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
The following things happen here:
- retrieving the new state by subscribing in the constructor
- deriving the values from the
page
param fromthis.store
by using theselect
method - we store the returned subscription from the
subscribe
call undersubscription
On the next change detection run, we will see the latest emitted value in the view.
Here we have to manage the subscription in case the component gets destroyed.
- when the component gets destroyed
- we call
this.subscription.unsubscribe()
in thengOnDestroy
life-cycle hook.
- we call
Patterns to avoid observables
As these examples are very simple let me summarise the learning with a broader view.
Where to subscribe
We saw that we subscribe to observables in different places.
Let's get a quick overview of the different options where we could subscribe.
- constructor
- ngOnChanges
- ngOnInit
- ngAfterContentInit
- ngAfterContentChecked
- subscription over
async
pipe with a template binding - subscription over
async
pipe with a template expression - ngAfterViewInit
- ngAfterViewChecked
- ngOnDestroy
If we take another look at the above code examples we realize that we put our subscription in the constructor to avoid reactive programming.
And we put the subscription in the template when we leveraged reactive programming.
This is the critical thing, the subscription.
The subscription is the place where values are "dripping" out of the observable.
It's the moment we start to mutate the properties of a component in an imperative way.
In RxJS
.subscribe()
is where reactive programming ends
So the worst thing you could do to avoid reactive programming is to use the async
pipe.
Let me give you a quick illustration of this learning:
We learned the following:
- If we want to avoid reactive programming we have to
subscribe as early as possible, i. e. in the
constructor
. - If we want to leverage reactive programming we have to subscribe as late as possible, i. e. in the template.
As the last thing to mention here is a thing that I discover a lot when I consult projects is mixing the styles.
Until now I saw plenty of them and it was always a mess.
So as a suggestion from my side tries to avoid mixing styles as good as possible.
Make it even easier
We realized that there is a bit of boilerplate to write to get the values out of the observable.
In some cases, we also need to manage the subscription according to the component's lifetime.
As this is annoying or even tricky, if we forget to unsubscribe, we could create some helper functions to do so.
We could... But let's first look at some solutions out there.
In recent times 2 people presented automation of something that I call "binding an observable to a property" for Angular components.
Both of them created a HOC for it in a different way. (HOC is an acronym and stands for Higher Order Components)
@EliranEliassy presented the "@Unsubscriber" decorator in his presentation đŒ Everything you need to know about Ivy
@MikeRyanDev presented the "ReactiveComponent" and its "connect" method in his presentation đŒ Building with Ivy: rethinking reactive Angular
Both of them eliminate the need to assign incoming values to a component property as well as manage the subscription.
The great thing about it is that we can solve our problem with a one-liner and can switch to imperative programming without having any troubles.
That functionality could be implemented in various ways.
Eliran Eliassy used class-level decorators to accomplish it.
He uses a concept from functional programming, a closure function, that looks something like this:
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Unsubscriber } from 'unsubscriber.decorator.ts';
@Unsubscriber()
@Component({
selector: 'comp',
template: `
<h2>@Unsubscriber</h2>
Store value {{page}}
`
})
export class ExampleComponent {
page;
subscription = this.store.select(s => s.page)
.subscribe(page => this.page = page);
constructor(private store: Store<any>) {
}
}
Now let's take a look at Mike Ryanâs example:
Mike used inheritance as an implementation approach. He also listed the various ways of implementation in his talk, so you should definitely watch it!
In addition to Eliran's example here the subscription call is also invisible, which is even better!
This is also the cleanest solution I have found so far.
He used a concept form object oriented programming, inheritance, that looks something like this:
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { ReactiveComponent } from 'reactive.component.ts';
@Component({
selector: 'comp',
template: `
<h2>ReactiveComponent</h2>
Store value {{state.page}}
`
})
export class ExampleComponent extends ReactiveComponent {
state = this.connect({page: this.store.select(s => s.page)});
constructor(private store: Store<any>) {
}
}
As both versions are written for Ivy
you might wonder how to use it right now?
I can inform you that there are also several other libs out there for ViewEngine
.
There is i.e. đŠ ngx-take-until-destroy and đŠ ngx-auto-unsubscribe from @NetanelBasal
With this information, we could stop here and start avoiding reactive programming like a pro. ;)
But let's have the last section before we make our conclusions.
Composing asynchronous processes
In this section, we will compose values from the Store
with results from HTTP requests and render it in the template.
As we want to avoid broken UI state we have to handle race-conditions.
Even if there is no user interaction we refresh the result every 10 seconds automatically.
Also, if the component gets destroyed while a request is pending we don't process the result anymore.
As I mentioned that it maybe makes no sense to have the HTTP request as an observable I will use the browserâs fetch API instead of HTTPClient
to fire the HTTP request.
Leveraging Reactive Programming (đź demo)
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { merge, interval } from 'rxjs';
import { map, switchMap, withLatestFrom } from 'rxjs/operators';
import { Store } from '@ngrx/store';
@Component({
selector: 'example4-rx',
template: `
<h2>Example4 - Leverage Reactive Programming</h2>
Repositories Page [{{page | async}}]:
<ul>
<li *ngFor="let name of names | async">{{name}}</li>
</ul>
`
})
export class Example4RxComponent {
page = this.store.select(s => s.page);
names = merge(this.page, interval(10000).pipe(withLatestFrom(this.page, (_, page) => page)) )
.pipe(
switchMap(page => this.http.get(`https://api.github.com/orgs/ReactiveX/repos?page=${page}&per_page=5`)),
map(res => res.map(i => i.name))
);
constructor(private store: Store<any>, private http: HttpClient) {
}
}
Following things (roughly) happen here:
- all values are retrieved by using the
async
pipe in the template - deriving the values from the
page
param fromthis.store
by using theselect
method - deriving the HTTP result by combining the page observable with the HTTP observable
- solving race conditions by using the
switchMap
operator - as all subscriptions are done by the
async
pipe we:- subscribe to all observables on
AfterContentChecked
- applying all arriving values to the pipes return value
- subscribe to all observables on
On the next change detection run, we will see the latest emitted value in the template.
If the component gets destroyed Angular manages the subscription over the async
pipe.
Avoiding Reactive Programming (đź demo)
import { Component, OnDestroy } from '@angular/core';
import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs';
@Component({
selector: 'example4-im',
template: `
<h2>Example4 - Avoid Reactive Programming</h2>
Repositories Page [{{page}}]:
<ul>
<li *ngFor="let name of names">{{name}}</li>
</ul>
`
})
export class Example4ImComponent implements OnDestroy {
pageSub = new Subscription();
page;
intervalId;
names;
constructor(private store: Store<any>) {
this.pageSub = this.store.select(s => s.page)
.subscribe(page => {
this.page = page;
this.updateList()
});
this.intervalId = setInterval(() => {
this.updateList();
}, 10000)
}
updateList() {
if(this.page === undefined) {
return;
}
fetch(`https://api.github.com/orgs/ReactiveX/repos?page=${this.page}&per_page=5`)
.then(result => result.json())
.then((res: any) => this.names = res.map(i => i.name));
}
ngOnDestroy() {
this.pageSub.unsubscribe();
clearInterval(this.intervalId);
}
}
The following things happen here:
- retrieving the new state by subscribing in the constructor
- deriving the values from the
page
param and fetch data fromthis.store
by using theselect
method and call subscribe - storing the returned subscription from the
subscribe
call underpageSub
- in the store subscription we:
- assign the arriving value to the components
page
property - we take the page value and create an HTTP
get
call by using thefetch
API. - an HTTP
get
request fires - we retrieve the result in
.then()
call of the returnedPromise
- we convert the response to
JSON
format - we assign the value to a static class property
- we convert the response to
- assign the arriving value to the components
Here we have to manage the active processes in case the component gets destroyed.
- when the component gets destroyed
- we call
this.pageSub.unsubscribe()
in thengOnDestroy
life-cycle hook - we call
clearInterval(intervalId);
in thengOnDestroy
life-cycle hook
- we call
As I nearly always code reactive in Angular projects I never think about teardown logic.
Therefore I forgot that a tiny bit of logic made a critical mistake. blush
I forgot to dispose of the returned Promise
from the fetch call, and therefore I didn't handle race conditions and we have a bug in our code.
So I also implemented a solution for the race condition of the HTTP calls.
To solve it I used another API, the AbortController
's signal
and abort
functions.
I created a method on my component disposableFetch
and a property httpAbortController
.
The method takes the URL and a callback as a second parameter.
It fires the request, provides the signal from the created AbortController
to the fetch
call and passes the result to the callback function.
Then it returns the created AbortController
to give others the option to dispose of the fetch call.
Avoiding Reactive Programming Fixed (đź demo)
import { Component, OnDestroy } from '@angular/core';
import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs';
@Component({
selector: 'example4-pr',
template: `
<h2>Example4 - Use Promises</h2>
Repositories Page [{{page}}]:
<ul>
<li *ngFor="let name of names">{{name}}</li>
</ul>
`
})
export class Example4ImFixedComponent implements OnDestroy {
pageSub = new Subscription();
page;
intervalId;
httpAbortController;
names;
constructor(private store: Store<any>) {
this.pageSub = this.store.select(s => s.page)
.subscribe(page => {
this.page = page;
this.updateList()
});
this.intervalId = setInterval(() => {
this.updateList();
}, 10000);
}
updateList() {
if(this.page === undefined) {
return;
}
if(this.httpAbortController) {
this.httpAbortController.abort();
this.httpAbortController = undefined;
}
this.httpAbortController = this.disposableFetch(
`https://api.github.com/orgs/ReactiveX/repos?page=${this.page}&per_page=5`,
(res: any) => this.names = res.map(i => i.name));
}
ngOnDestroy() {
this.pageSub.unsubscribe();
clearInterval(this.intervalId);
if(this.httpAbortController) {
this.httpAbortController.abort();
this.httpAbortController = undefined;
}
}
disposableFetch(url, callback): AbortController {
const httpController = new AbortController();
const httpSignal = httpController.signal;
fetch(url, {signal: httpSignal})
.then(result => result.json())
.then(callback);
return httpController;
}
}
The following things happen here:
- retrieving the new state by subscribing in the constructor
- deriving the values from the
page
param fromthis.store
by using theselect
method call subscribe - we store the returned subscription from the
subscribe
call underpageSub
- in the store subscription we:
- assign the arriving value to the components
page
property - we take the page value and create an HTTP
get
call over the newdisposableFetch
method` - if
httpAbortController
is aAbortController
we callAbortController.abort()
- we reset
httpAbortController
toundefined
- we reset
- we create a new
AbortController
and provide its signal to thefetch
call - we use the
fetch
API again- an HTTP
get
request fires - we retrieve the result in
.then()
call of the returnedPromise
- we convert the response to
JSON
format - we assign the value to the static class property
- we convert the response to
- an HTTP
- we store the
AbortController
returned bydisposableFetch
at under a component property
- assign the arriving value to the components
Here we have to manage the active processes in case the component gets destroyed.
- when the component gets destroyed
- we call
this.pageSub.unsubscribe()
in thengOnDestroy
life-cycle hook - we call
clearInterval(intervalId);
in thengOnDestroy
life-cycle hook - we call
httpAbortController.abort();
in thengOnDestroy
life-cycle hook
- we call
My 2 cents
I got told so many times that you have to learn RxJS
to be able to use Angular.
Even if it was easy for me to avoid it, it doesnât seem easy to other people... This made me create this writing.
It hopefully showed that it is very easy to avoid reactive programming in Angular (even if you use reactive third party libs).
Nevertheless, I want to share my personal opinion and experience with you.
RxJS gave me a hard time learning it, but the code I produced with it was (mostly ;P) more maintainable, stable and elegant than any other.
Especially in the front-end, it gives me a tool to model and compost complex asynchronous processes in a way that impresses me even today.
If we take the above examples we see that if we don't use observables we:
- produce more lines of code
- the level of complexity in the code is much higher
- we have to put the logic for one process in multiple places
- it is very hard to maintain the code
- it is very hard to add features
- even the number of indentations in the textual process description is way deeper
IMHO it is worth the headache, that you will for sure get if you try to learn RxJS
and even more worth the money that the company spends on your learning.
Btw, I do trainings and workshops ;)
Summary
We used different APIs for the imperative approach:
-
addEventListener
andremoveEventListener
-
new Promise
andno dispose logic implemented
-
new AbortController
andAbortController.abort
-
setInterval
asclearInterval
instead of the reactive (functional reactive) approach:
-
subscribe
andunsubscribe
And we maintained the state in a mutable
instead of an immutable
way.
How to avoiding reactive programming in angular:
- Calling
.subscribe()
ends reactive programming. Do it as early as possible! - You can use helpers to hide away the subscription handling
- Don't mix it!
- Functional Reactive Programming is a lot of headaches and has steep learning curve
- IMHO FRP pays off quickly if you need to compose asynchronous processes
Thanks to @niklas_wortmann for the really helpful feedback and review <3 <3 <3
Resources
You can find the source code of the examples
as well as all the resources in the repository How to Avoid Observables in Angular on GitHub.
The all in one solution to handle Observables and subscriptions:
đŠ @rx-angular/state
For Ivy (Angular >= 9):
For ViewEngine (Angular <= 8):
Glossary
- functional programming: Using functions and immutable state
- reactive programming: A style of functional programming where we process incoming events as we would do with lists (JavaScript Arrays)
- imperative programming: Using objects and mutable state
- single-shot observable: As a Promise completes after the value it emitted, these observables emit a single value and then complete.
- on-going observable: like an interval fires multiple values over time these are observables that need to get completed manually.
- functional composition: Therm in functional programming that is a mechanism to combine simple functions to build more complicated ones.
- closure: Therm in functional programming that is a function storing a value together with another function scope.
- HOC: Acronym for Higher Order Component
- inheritance: Is the mechanism of basing a class instance upon another one, to retaining similar implementation
- class-level decorators: TypeScript Documantation
- broken UI state: An inconsistency in rendered state and stored state
- race-condition: Problem of asynchronous programming where two or more operations are accidentally done in the wrong sequence.
Top comments (18)
Seems like an anti-pattern to go out of your way to avoid using observables with Angular and there doesnât appear to be any strong benefit in doing so other than laziness in learning something new.
Why would you want to avoid functional programming? đ€
It seems like Angular is not the appropriate framework for you then.
Not sure i would put Angular in the functional programming camp based on how it is used, classes for components etc.
But it becomes a lot more to use if you mix FP into it tho. :D
I'm a big fan of react, and it happens that I've been working with angular for more than two years now (you can't always choose đ ).
At first, I've hated Observable because I was not able to understand and works with it, and also because frankly the documentation is not for beginners. Took me around 1 year to master and use Observable with ease.
I've tried mostly all the things written in this article and also read a lot of things, like "functional programming with angular", "store pattern in angular", and now "avoid observable in angular".
But please, if you don't like Observable or OOP, then angular is probably not for you, just use something else.
Tweaking, in my experience, always results into a mess.
nice article. Think you have a little typo
result
would be assigned the subscription, even though you later override it in thesubscribe()
. I think you mean this:Right!
One more logical thing I would rethink: mentioning where to subscribe: ngOnchanges and ...Check hooks are bad places to subscribe since to create onbservabke every time they are called
How about just do it like this :
async foo() {
this.user = await observable.pipe(take(1)).toPromise();
// this.user.id works here
}
I only registered to this site, to say Thank You for this comment of yours. I've had trying to figure out how to to this and your comment ended a 7 hours long search. Thanks!
I really don't get why this isn't mentioned more. This way you can easily escape the observable horror when you don't need it. Also async await makes you code so much cleaner, most examples use .then() for some reason.
It's just true!
It seems that won't be an option in the future: indepth.dev/rxjs-heads-up-topromis...
Good article, Michal. I would only suggest a modification in the title because it sounds pretty confusing. Juniors tend to confuse Observables (object) with RxJS (library) and reactive programming (paradigm). Thus it's quite easy for people that read titles upfront to digest the content based on such statement.
The fact is because the Observable object is not avoided at all. They are assigned to the class variables in all examples, what we may consider that they still are in use. Only the subscription is avoided to be directly used in the component implementation.
Great poin.
I was going to say:
What about ChangeDetectionStrategy OnPush? We should only throw it in the trash?
We can see that you are doing it really wrong not caring about CPU and digest cicles.
Did you ever write any component, with good performance, that handles a lot of events (click, scroll) without using rxjs and OnPush strategy?
This does not make any sense IMO.
Please, study about OnPush and Async pipe instead of trying to creat such approach.
But you showed a lot of edges of Reactive Programming that takes a lot of time to understand and use with easy.
Sorry
Epic! đ I totally agree with you and based on my experience I do confirm that avoiding frp leads to messy code which is hard to reason about. Unfortunately most companies simply wants to get work done asap and do not care about maintenence. Local dev heroes create non frp code which makes adding new features like playing a jenga tower game đ one question which I already as asked you during workshop in Warsaw (but the answer was to subscribe which is in contrary to the art đ):
How to handle the scenrio: land on the details page and want to create a form and further submit it. Form group is easy to create in a reactive way but upon submission I need the entity id which is kept in the activated route observable. Any idea how to get it without subscribe? đ
Lol. Old reverse "IMO, don't do this" right at the end. I strongly agree. One thing not considered is the race conditions you create when you try to do logic on things you've pulled out of observables.
Might want to lead with the fact that this is a bit of an anti-pattern.
I love the comparisons of different techniques, this gives me a much better feel for the fundamentals of observables overall.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.