Skip to content
View lessons Login
3d cartoon hands holding a phone

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:

Play

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.

Play

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.

STANDARD STANDARD

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).