
Existing member? Log in and continue learning

See if you like it, start the course for free!

Unlock full course by purchasing a membership
Coding Reactively/Declaratively in Angular
Coding Reactively/Declaratively in Angular
We are going to talk about what reactive/declarative coding in Angular means mostly at a conceptual level in this lesson. But, these definitions and discussions can feel disconnected from the practical side of actually building applications this way. You are going to hear a lot of stuff that might sound… Interesting? Useful? Pointless? But it can be hard to get a sense of what it actually means, what code will you actually write to adhere to these idealistic descriptions.
Why are we even bothering with this nonsense? We should just build things the obvious way and get the job done. We’re here to build apps, not write pretty code!
These are all common thoughts about declarative code, and I will not blame you for having the same thoughts yourself.
The trouble with teaching the benefits of reactive and declarative code is that for most people it is not as intuitive as the alternative (imperative code) in the beginning. The benefits of declarative code also only really become apparent when the bigger picture is taken into account — reactive/declarative code rarely looks beneficial when looking at isolated examples. It is only when you consider the application in a more broad context that the benefits become apparent.
As we progress through this course, we will be coding and building applications using this philosophy, so hopefully you will be able to build up a strong sense of what it all means as we go. But, to give you some sense of grounding in the practical side of things before we jump too heavily into concepts I will attempt a bare bones definition of what I think reactive/declarative coding actually looks like in Angular at a basic level.
Again, these videos are not a required part of this course, but they may help build additional context around what “declarative” actually means. The first focuses on the “why” of declarative code:
and the second focuses on how we can adhere to the fundamental idea behind coding declaratively — not reassigning values after they have been declared — but still have our application actually do things.
By this point, we already have some sense of what reactive means… by I keep using these terms together: reactive/declarative. I would argue that writing declarative code is actually the most important aspect here, reactive code and patterns just facilitate writing declarative code.
There are different ways we can think about the definition of declarative code, some of which we will talk about in this lesson, but I will now give you what I think is the most direct and simple definition of how we will be coding declaratively.
This is a declaration:
comments = ['hi', 'hello', 'how are you?']
We have declared a class member called comments
. What is comments
?
We can answer that just by looking at its declaration. It is an array
containing three strings: hi
, hello
, and how are you
.
We have declarative code with no need for reactive mechanisms like observables or signals.
But what if we wanted to create another declaration that contained only the last comment in the array? No problem:
comments = ['hi', 'hello', 'how are you?']lastComment = comments[comments.length - 1];
We are still being completely declarative here. What is lastComment
? Just
by looking at the declaration of lastComment
I know exactly what it is — it
has the value of whatever the last item in the comments
array is. I also know
exactly what the comments
array is just by looking at its declaration.
This is all declarative.
But this makes the assumption that this data will never change. What happens if we do this:
comments = ['hi', 'hello', 'how are you?']lastComment = comments[comments.length - 1];
someMethod(){ this.comments.push('uh oh');}
Now everything has fallen apart. Can I look at comments
and know what it
is just by looking at its declaration? No. Not anymore. I know what it is
initially, but to understand its full behaviour over time I need to find
anywhere else in the code where it is being imperatively changed. Our
someMethod
imperatively changes what comments
is.
There is also a problem with lastComment
now. It’s value is only ever set to
what comments
was initially — when comments
is updated, lastComment
is
not updated. We need to do that manually… or imperatively:
comments = ['hi', 'hello', 'how are you?']lastComment = comments[comments.length - 1];
someMethod(){ this.comments.push('uh oh'); this.lastComment = this.comments[this.comments.length - 1];}
We are already getting a preview of the benefits of declarative code — the
imperative code above is super prone to bugs. If we forget to manually update
lastComment
it will not have the correct value.
We can fix this with signals and make it more declarative:
comments = signal(['hi', 'hello', 'how are you?'])lastComment = computed(() => this.comments()[this.comments().length - 1])
someMethod(){ this.comments.update((comments) => ([...comments, 'a bit better']))}
This is still not completely declarative — we are still imperatively updating
comments
. How can we tell this is imperative? Because we don’t really know
what comments
is unless we also find and understand what someMethod
is
doing. We can’t just look at the declaration of comments
.
But, our lastComment
is declarative and now it will be automatically updated
when comments
updates without us needing to do it manually.
You will see as we progress that we do this sort of “cheating” quite a lot. An Angular application can not really be “fully” declarative. Often we will have some small imperative operations happening, and then have everything after that point be reactive/declarative.
But wait a minute! Isn’t this a module about observables? Signals can help us be declarative for situations dealing with synchronous reactivity which is what the above example is — we don’t need to wait for anything to complete.
But observables are what can help us be declarative when dealing with asynchronous reactivity. Let’s jump right to a more advanced example to highlight the power of RxJS and observables.
We might have some declarative code like this:
articlesForPage$ = this.currentPage$.pipe( startWith(1), switchMap((page) => this.apiService.getArticlesByPage(page).pipe( retry(5) ) ))
firstArticle$ = this.articlesForPage$.pipe( map((articles) => articles[0]))
Let’s apply our declarative test — can we understand what articlesForPage$
is just by looking at its declaration? It is much more complex now, but yes.
It takes whatever the currentPage$
is and makes a request to the API to load
the data for that specific page. If currentPage$
has not emitted any values
yet it will start with a page number of 1
by default, and if the request fails
it will retry
the request 5
times before giving up.
The end result is that articlesForPage$
is the articles for the current page,
and we can see exactly how it is calculated. There is no manual handling or
setting of the articlesForPage$
data outside of its declaration — everything
is right there in a nice little box.
This is what we need observables for. We could use signals for a similar situation, but it would not be declarative because signals can not handle asynchronous reactivity. We would have to execute the HTTP request and then imperatively update a signal. We would also need to imperatively handle dealing with the retries as well.
The same goes for firstArticle$
— everything that it is and ever will
be is defined right there in the declaration. Since firstArticle$
does not
deal with any asynchronous operations, this one could technically be a signal if
we wanted it to be:
articlesForPage$ = this.currentPage$.pipe( startWith(1), switchMap((page) => this.apiService.getArticlesByPage(page).pipe( retry(5) ) ))
articlesForPage = toSignal(this.articlesForPage$);
firstArticle = computed(() => { this.articlesForPage()[0];})
We are going to talk a lot more about when/how to combine signals and observables together in the state management lesson.
It is this combining of multiple declarative values together where the real power is. We can just have these long interconnected chains of everything just automatically reacting to the things it depends on changing — no need to worry about imperative updates or forgetting to manually refresh some value that you didn’t realise depends on the thing you just updated.
The above is really the most important take away from this lesson — in the rest of the lesson we are going to cover a bit more about the concept of “reactivity” in more general and perhaps more esoteric terms.
What does Reactive mean exactly?
Different people have a lot of different interpretations of what “reactive” means.
Thanks for checking out the preview of this lesson!
You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).