Reactive programming in Angular

Jorge Solis
12 min readApr 16, 2022

--

After 7 years of working with Angular, I faced a couple of big projects using something that actually is in the core of the other big frontend framework, React. It’s named reactive programming, and consist briefly in a serie of tools to manage data as stream and show only when it changes. So far so good, someone can tell that that’s exactly what Angular does, bind data to visual components and show it. But of course is not the same, and to be fair, was something I never did on Angular (but in React from the very beginning)

My goal is to consider the challenges for a traditional Angular developer more than debate reactive programming, but we can’t debate if we don’t set the a few points as the basis. So let’s go for it.

Functional programming is the new (old) black

Functional programming, after years of OOP as the big player, has come back evolving from his procedural roots to claim his rights in the software industry. Over the last decade, more and more frameworks and languages begin to model code in a different direction and if you had the chance to work with Clojure, Scala or Go probably you know what I’m talking about. Those like me that begin at the end of the past century (I like how it sounds :) probably had the chance to work the procedural approach, with languages like C, Perl or PHP building the very first pieces of the internet. But a decade later, probably OOP trough Java or .Net would have been your initial language.

Functional programming as his name state, is based on functions that talk to each other and not really in the data structure. Data tend to be inmutable and stateless by default, functions are the first-class citizen in this world and pipe together functions that talk to each other is the straight way of getting results

There are tons of graphics that describe the differences and I encourage to search for them, but this one is particularly effective in spot the mindset behind each one, the procedural origin of functional programming Vs the OOP approach( source https://dare2compete.com/blog/differences-between-procedural-and-object-oriented-programming that I recommend to read )

I will assume that if you work with Angular, OOP is more familiar to you and will skip the class/inheritance/interface/event/design-pattern explanation based on objects, methods and properties. I just want to borrow from the graphic the way a single interaction is described on each case. While the procedural explain it as a chain of actions, the OOP approach describe it as a serie of objects that have properties that allow them to interact properly.

Stateless sounds weird

If you have been working in Angular for a while, the super summarized notion about functional programming would have sound strange. Data binding, data manipulation, state management, classes and components are the base of the daily work, how could be that the opposite is doable? Well, to be fair you don’t resign the foundation elements, but you add a new approach to organize your application differently. And that is disturbing, and incomprehensible at first sight, specially when you face the stream data manipulation using and old friend, the RxJS library present in Angular since the very early versions. look at this code:

public retrieveBasket$ = createEffect(() =>combineLatest([   this.actions$.pipe(ofType(MainAppActions.fetchInitData)),   this.actions$.pipe(ofType(FeatureActions.initFeatures))]).pipe(  take(1),  filter(() => !isSellingOrBuyingFlow()),  mapTo(BasketActions.retrieveBasket()) ));

Not that difficult, it combines a couple of different streams, take the first combined value, filter it and map to another action. It uses NgRX library (more on that later) but my point here is … wtf … I never used code like this!

The real thing here is that reactive programming works subscribing asynchronously to streams of data. And this could be done whitout any external library, just RxJS. How? Here’s a simple example:

const streamobj = interval(1000)
.pipe(
take(5),
tap(i => console.log("streamobj value "+ i) )
);
streamobj.subscribe(value => console.log("observer received " + value));

Here we subscribe to a stream source that emits a value every second, take 5 elements and finish the stream. Subscribe is something that we do usually on external calls, and probably no extraneous for you, and this is the base. But you have a lot of operators in RxJS, around 100, here.s a list I borrow from the excellent Pluralsight course Learning RxJS Operators by Example Playbook by David Mann, that I highly encourage

RxJS operators list

I know, crazy list, but believe me, in the end you just use a few of them, around 10 average. The point is that all of these operators do a specific operation over a stream, and the way to visualize what they do is the marbles diagram, where streams and operators are explained. Below is the popular map function as explained on RxJS documentation

map operator diagram from RxJS documentation

Each of the marbles of the first stream are emitted, the map operation transform the values and the subscriber get the transformed value. The marbles diagram is used trough all the documentation to explain how you operate over the streams.

So, we subscribe to streams of data, listen to changes, show in the component … async streams manipulation is challenging, so why to use it? Let’s dive into this

Why to use reactive programming?

If you were out there for enough time on development, waves of new toys, frameworks, libraries and coding style have reach your door many times. After a few cycles of overenthusiasm-dissapointment, you learn to be cautious and if possible, don’t use any new tool around, or at least not to be the first to use it.

The first time I saw complex data stream manipulation in a big project, I realized that didn’t know that much about RxJS, and did my best to copy&paste in an orderly fashion, and it works because if you don’t understand, just follow the structure, and some point you will.

But I never found the reason to use in Angular until I face my first React project, transitioning a project from class base components to functional components. If you never heard of that, I will copy over a skeleton for a React component in both styles

/**********************
* Class based component
***********************/
class Space extends React.Component {
constructor(props) { ... }

componentDidMount() {
window.addEventListener('resize', () => {
const stars = createStars(window.innerWidth, this.props.color);
this.setState({ stars });
});
}

render() { ... }
}

On February 16, 2019, React 16.8 was released to the public, and introduce hooks, that allow the full change to functional style like this

/****************************
* Functional based component
*****************************/
const Space = ({ color }) => {
const [stars, setStars] = useState(createStars(window.innerWidth));

useEffect(() => {
window.addEventListener('resize', () => {
const stars = createStars(window.innerWidth, color);
setStars(stars);
});
}, []); // <-- Note the empty array

return (
...
);
};

Note that instead of a full class, they switch to functions based on some toolset to manage data and side effects. I don’t want to explain this, is just to give you the taste, and probably if you were not a consultancy warrior (aka use whatever technology they trow at you) you didn’t have the need or the chance to face this.

But let’s go back to Angular and what this has to do with it. First thing you probably already know: they look at each other to catch on any new stuff as fast as possible. Even at some point they married and have a child, named Vue, but I think after that they divorce and nobody claims it. Jokes apart, there are a few valid points to use reactive programming, let’s do a list

  • Direct subscription to services from inside components coupled them to implementation
  • Parsing and sometimes adapting data to display needs introduce a lot of logic inside components.
  • Directly related with the previous point, business logic is handled inside components
  • Shared data is spread and difficult to track
  • In general, coupled implementations force to apply any change trough many components, and bug tracking becomes harder

Following this concerns, reactive programming propose

  • Services are observables treated as data stream with the help of the async tools available in RxJS
  • Angular services are stateless and mutable state variables are discouraged
  • State still exist of course, but mantained by the components as per visual needs
  • Centralized store for data as a single source of truth
  • Components use async pipe to subscribe and show data whitout much logic inside

Additionally, with the help of external libraries like Ngrx or Ngxs the structure of the code is more previsible and handling growing business rule becomes routinary. So your project need to use reactive programming? Let’s move to this and consider a few matters

Should I use reactive programming?

Is not common to start from scratch and be able to select what to use, but is that the case, you should carefully evaluate if reactive programming would add some value to your project. Angular devs are not used to this functional approach, so consider that this probably will add some learning curve to your team. And that’s the first key: is your application complex? What I mean with complex … let’s walk trough a few points

  • Your application use a lot of external data, interact frequently with services, need to manage and show specific data to the user
  • Business rules are significant, and a lot of entities need to follow different restrictions that are not obvious nor implied by the data itself
  • The components of the applications share a similar data, probably showing different aspects or focusing in specific user actions
  • Growth of data, users interactions or business rule are expected
  • Data should be persistent and available when entering different routes
  • There’s a team of developers that need to work coordinately and need to br predictable
  • The company have a size enough to have many departments pushing for new features (promotions, marketing tracking, data analysis, user profiling, etc)

So general speaking, no for small projects, no for non intensive-data-driven apps, no for simple enough business rules, no for apps that will no scale. Benefits come at a cost of some complexity, boilerplate, and probably some learning curve, so consider the expertise of your team as well.

For those that are still curious, lets draw a few points about this approach to finish this analysis

Structure of a reactive application using ngrx

Ngrx logo

As stated you can use reactive programming just trough RxJS tools that already exist in Angular. But if you decide to move on this architecture, your project probably will need some clear structure pattern to follow. Here libraries like Ngrx comes into play, adding specific tools to reduce boilerplate and structure the whole skeleton. Notably you will have a Store as single source of truth where all your data lives, selectors to slice the data, actions to manage your interactions, effects to handle your services and reducers to conduit the business logic. All together these elements handle the business logic in a centralized and structured way, feeding component with the appropriate data and alleviating the pain of constant change and growth.

Here’s a summary description of these key elements

  • Store
    As a single source of truth, NgRx Store provides state management for creating maintainable, explicit applications through the use of single state and actions in order to express state changes
  • Actions
    Actions express unique events that happen throughout your application and solve component interaction and communication.
  • Reducers
    Reducers are responsible for handling transitions from one state to the next state in your application. Reducer functions handle these transitions by determining which actions to handle based on the action’s type.
  • Selectors
    Selectors are pure functions used for obtaining slices of store state. Selectors are memoized and composable, so all the data filtering/manipulation is done here to feed the component with what it needs to be display
  • Effects
    Effects are an RxJS powered side effect model for Store. Effects use streams to provide new sources of actions to reduce state based on external interactions such as network requests, web socket messages and time-based events.

So new filters or rules to show data? Create a new selector. Api changes on the backend? Modify or add an effect. New promotion and user interactions? Add a few actions and handle in a reducer. No need to go over components modifying visual stuff and internal logic everywhere.

Give me an example !

Ok, I don’t want to go deep in an example because this is a wide view of the matter and a more specific hands on is necessary here, but hey, this is an article already long enough !

So this is a Home page of an application that features different courses, organized in clases, full courses or thematic masters. This homepage component handle the display of all this information as small cards in a few scrollable container

import { Component, OnInit } from '@angular/core';
import { Tarjeta } from '../model/dto';
import { ApiService } from '../services/api.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css'],
})
export class HomeComponent implements OnInit {public clases: Object[] = [];
public courses: Object[] = [];
public masters: Object[] = [];
public loaded = false;
constructor(private _api: ApiService) {
// load data from backend
this._api.getEntity('homeresumen', {
column: 'Inicio',
order: 'DESC'
}).subscribe((result) => this.onCursos(result));
}
// data comes as tree: clases inside cursos as an array, so more
// parsing will be needed if users drill down
onCursos(result) {
this.clases = [];
this.courses = [];
this.masters = [];

result.data.forEach((element) => {
const item: ICourse = {
id: element.PkCourse,
Name: element.name,
Resume: element.Resume,
Price: element.Price,
FkCourseType: element.FkCourseType
};
if (element.FkCourseType === 1) {
this.courses.push(new Tarjeta(item));
} else if(element.FkCourseType === 3){
this.clases.push(new Tarjeta(item))
} else {
this.masters.push(new Tarjeta(item));
}
});
this.loaded = true;
}
( ... more code not essential for this example ... )
}

Now this components knows the API, which method to call and also the structure of the expected data. So once the data is loaded, it walk trough it, creating the items and separating them in different list based on the type. So this components knows also the data structure and build the pieces of information to display. They are also specific pages for each of the types, that call different methods and need less parsing because it only focuses on one of them. Now imagine a new “Teacher” column comes in the specs, so what you do? Go over all components that show courses, classes, masters or all together and modify the parsing adding one element. Furthermore development add components showing this and change become more and more cumbersome.

Let’s try a different approach: an action fires a side effect, that load data from the backend, put in the store and select the proper data to be shown in the component. Will omit for brevity the full implementation and just show how the component looks now

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Tarjeta } from '../model/dto';
import { select, Store } from '@ngrx/store';
import { State } from 'src/app/state/app.state';
import { Observable } from 'rxjs';
import { CourseActions } from 'src/app/state/actions';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HomeComponent implements OnInit {public clases$: Observable<ICourse | null>;
public courses$: Observable<ICourse | null>;
public masters$: Observable<ICourse | null>;
constructor(private store:Store<State>) {
// load data from backend
this.store.dispatch(CourseActions.loadCourses());
}
ngOnInit() {
this.clases$ = this.store.pipe(select(getClases));
this.courses$ = this.store.pipe(select(getCourses));
this.masters$ = this.store.pipe(select(getMasters));
}

( ... more code not essential for this example ... )
}

A few things to note. The changeDetection strategy change since by default Angular is listening always for changes, but in this scenario the stream data push the changes, so the component is not always checking for changes. The handler for data coming (if necessary) is ngOnChanges and the SimpleChange object

Note that no logic regarding the API of the data structure is handled now in the component itself, that just dispatch actions and subscribe to data. No matter how data or business rule change, no need to modify the component except for new visual elements. There’s an important boilerplate related to this that we omit, but all of them (actions, selectors, effects) are in a single state folder, and anyone can follow it in a single place.

Last key piece is the Redux devtools that you can install in your browser to follow all app interactions and data store

Dev tools on chrome showing actions on the left and Store data on the right

This is not only useful for debugging purpose, but also as documentation of what the application is doing with the data, and the full store is exportable as a single complex json object. So no guessing but facts.

Conclusion(s)

Adopt a reactive approach is not applicable to any project, and you need to carefully weight pro and cons. In Angular, this approach is growing slowly as the complexity in mantain big applications (where the structured approach of Angular as a full ecosystem make the ideal tool) arise.

Developers that never face async data stream manipulation probably would need to invest some time in getting familiar with this concepts, that will become strong at enterprise level applications.

Sorry for the long article, I always promise to be shorter next time, but even general discussions like this one takes some space. If you have any thought on this just leave your comment, your input is always welcome!

--

--

Jorge Solis

Fullstack developer with more than 20 years of experience, having fun in small companies and following procedures in big ones