Wrapping Imperative APIs in Angular
Tags: angular, rxjs, reactive, stateadapt
Published: 2022-07-19 09:11:42

---
title: Wrapping Imperative APIs in Angular
published: true
description: Easily guaranteeing consistency by wrapping imperative APIs in Angular.
tags: angular, rxjs, reactive, stateadapt
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zgcqzsoqm1un85g70ag4.png
series: Progressive Reactivity in Angular
---

> This series explores how we can keep code declarative as we adapt features to progressively higher levels of complexity.

---

![divider](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/46rby7k8kpgqa11khbbf.png)

## Progressive Reactivity Rule #3

**Wrap imperative APIs with declarative ones.**

Imperative APIs are better than no APIs, and they tend to precede declarative APIs. Why is that, and what can we do about it?

Declarative code is more comprehensible than imperative code, as you saw in the example in [the first article in this series](https://dev.to/this-is-angular/progressive-reactivity-in-angular-1d40). But in order to write comprehensible code, you have to comprehend what you're writing. For example, it's easy to declare a variable with a bad name, but writing a comprehensible name requires a comprehension of what that variable represents. 

When developers solve difficult or novel problems (like creating a new framework) they lean towards an imperative style of programming, because it is easier and they are used to thinking imperatively. The imperative APIs cause applications that use them to become more imperative as well, which then grow into incomprehensible balls of spaghetti code. Inevitably the community creates declarative wrappers for the APIs, and then finally the APIs themselves are changed into something more declarative.

So, we should not be surprised or upset that Angular has plenty of imperative APIs. AngularJS was an early SPA framework, and was solving difficult and novel problems. In fact, AngularJS brought reactivity to DOM updates with change detection, and it was that very mechanism that created the problems that ended up being solved with the imperative APIs. And then Angular tried to maintain some continuity with AngularJS, so it inherited much of that imperative style.

Angular is unfairly disregarded by many developers who moved to React or another framework (yes, _framework_) after AngularJS, and have no actual clue about what modern Angular looks like. However, other modern frameworks have made progress that Angular has not been able to make. Although they are largely ignorant of the benefits of RxJS, they do have more many more declarative APIs than Angular, and that sometimes make me jealous.

## Modals

My favorite example is modals. In the Angular ecosystem, it seems a given that you have to open dialogs with an imperative `.open()` command. But it doesn't have to be this way. Literally every other component library in literally every other modern front-end framework has declarative dialogs that react to state, instead of depending on out-of-context imperative commands to open them. Don't believe me? Well, even if you do, I want to actually show you. Let's look at Vue, React, Svelte, Preact, Ember, Lit, Alpine and SolidJS. Feel free to skip to [Angular](#angular). It's a long list.

### Vue.js

[Top Vue Component Libraries](https://vue-community.org/guide/ecosystem/ui-libraries.html)

#### [Vuetify](https://vuetifyjs.com/en/components/dialogs/)

![Vue—Vuetify Dialogs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mvhc8geajyv2dkj613nn.png)

#### [Quasar](https://quasar.dev/vue-components/dialog)

![Vue—Quasar Dialogs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zs1f5xofy6y1aq8cd1vj.png)

#### [Bootstrap Vue](https://bootstrap-vue.org/docs/components/modal)

![Vue—Bootstrap Vue Modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rcovva2wuw5rmxf9617o.png)

### React

[Top React Component Libraries](https://technostacks.com/blog/react-component-libraries/)

#### [Material UI](https://mui.com/material-ui/react-dialog/)

![React—MUI Dialogs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dsb0ed9qlxggjrcxffts.png)

#### [Ant Design](https://ant.design/components/modal/)

![React—Ant Design Modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ntcpk400pgr0h8ak7b5.png)

#### [React Bootstrap](https://react-bootstrap.github.io/components/modal/)

![React—React Bootsrap Modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sarmyk5axiaw2p4bcbog.png)

### Svelte

[Top Svelte Component Libraries](https://studentsxstudents.com/5-best-svelte-ui-libraries-for-your-next-project-3632ca34daa8)

#### [Svelte Material UI](https://sveltematerialui.com/demo/dialog/)

![Svelte—Svelte Material UI Dialogs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jki36abhjbjkhtucz5s3.png)

#### [SvelteStrap](https://sveltestrap.js.org/?path=/story/components--modals)

![Svelte—SvelteStrap Modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iev13opskxthres4pn5e.png)

#### [Smelte](https://smeltejs.com/components/dialogs)

![Svelte—Smelte Modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cf6ulp4wvxvct0chlm1v.png)

### Preact

It was hard finding component libraries for Preact, to be honest. I've included the only one I found with documentation that was easy to find. 

#### [Preact Material](https://material.preactjs.com/component/dialog/)

![Preact—Preact Material Modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7i0ornxvl7cs19e229y.png)

I believe simply rendering the `Dialog` element opens it, so that is declarative.

### Ember

[Top Ember Component Libraries](https://emberobserver.com/categories/component-suites)

#### [Ember Paper](https://miguelcobain.github.io/ember-paper/#/components/dialog)

![Ember—Ember Paper Dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lw2hueuox598jn7y5iuc.png)

#### [Ember Frontile](https://frontile.dev/docs/overlays/modal)

![Ember—Frontile Modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpzx7waclv3bh4s990eh.png)

#### [SL Ember Components](https://sldn.softlayer.com/sl-ember-components/#/demos/sl-modal)

![Ember—SL Ember Components Modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jmpb6lvwapc5795qefx.png)

### Lit

Lit is for creating web components, so I will just look at [web component libraries](https://blog.bitsrc.io/9-web-component-ui-libraries-you-should-know-in-2019-9d4476c3f103) for this one.

#### [PolymerElements Paper Dialog](https://github.com/PolymerElements/paper-dialog)

![PolymerElements Paper Dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0k7l3lgydnu6zn0tdwr0.png)

#### [Vaadin Web Components](https://github.com/vaadin/web-components/tree/master/packages/dialog)

![Vaadin Web Components Dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wda8ne3r2kvao7vak28w.png)

#### [Wired Elements](https://github.com/rough-stuff/wired-elements/blob/master/docs/wired-dialog.md)

![Wired Elements Dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9ru89ehzv7pk6x594ri.png)

### Alpine

I only found [this example](https://alpinejs.dev/component/modal):

![Alpine Dialog 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uly5k3ua4mgz2olukq1i.png)

### SolidJS

SolidJS is an amazing library, but it is still very new. I couldn't find many component libraries with dialogs. But there is [this example on SolidJS's own website](Alpine Dialog), and it shows a modal being opened declaratively. I guarantee that any component library that pops up for SolidJS will be declarative like this.

![SolidJS Dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a6ctkvk8chy12xdn9i0p.png)

I did find [this unofficial component library for Headless UI](https://solid-headless.vercel.app/component/dialog):

![unofficial component library for Headless UI—Dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snod5y6akxnfs8rluvp8.png)

### Angular

Finally, Angular. [Top Angular Component Libraries]()

#### [Angular Material](https://material.angular.io/components/dialog/overview)

Ah, Angular Material, the official component library for Angular. Let's see how to use dialogs:

![Angular Material—Dialog HTML](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vzem28nzpsdwcetz0kzj.png)

Okay, so it's calling a method. That's breaks our [Rule 2](https://dev.to/this-is-angular/simple-derived-state-progressive-reactivity-in-angular-48oe#progressive-reactivity-rule-2). What does that method do? 

![Angular Material—Dialog TypeScript](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cx68iiq995qnx20rnp0.png)

This is the _first_ component library out of the 20+ for 7+ frameworks I've seen that opens dialogs imperatively. 

The 2nd and 3rd libraries are also imperative.

#### [ngx-bootstrap](https://valor-software.com/ngx-bootstrap/#/components/modals?tab=overview)

![Angular—ngx-bootstrap Modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5f3uwgns6optmmveedb1.png)

#### [ng-bootstrap](https://ng-bootstrap.github.io/#/components/modal/examples)

![Angular—ng-bootstrap Modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvcgnntfutbdeebtzyz6.png)

---

To summarize, 

| Framework      | Library 1 | Library 2 | Library 3 |
| ----------- | ----------- | ----------- | ----------- |
| Vue      | ✅ Declarative       | ✅ Declarative       | ✅ Declarative       |
| React   | ✅ Declarative        | ✅ Declarative        | ✅ Declarative        |
| Svelte   | ✅ Declarative        | ✅ Declarative        | ✅ Declarative        |
| Preact   | ✅ Declarative        | ✅ Declarative        | ✅ Declarative        |
| Ember   | ✅ Declarative        | ✅ Declarative        | ✅ Declarative        |
| Lit   | ✅ Declarative        | ✅ Declarative        | ✅ Declarative        |
| SolidJS   | ✅ Declarative        | ✅ Declarative        | ---        |
| Alpine   | ✅ Declarative        | ---       | ---        |
| Angular   | ❌ Imperative        | ❌ Imperative        | ❌ Imperative        |

## But you don't have to suffer.

Again, we should not be surprised or upset that Angular has plenty of imperative APIs. AngularJS was an early SPA framework, and was solving difficult and novel problems. 

But guess what else? The Angular team is not the pope. _You can have an opinion_, even if it goes against what the community assumes is correct because it is the default solution handed down from the beloved Angular team. 

So I created [a wrapper for Angular Material's dialog component](https://gist.github.com/mfp22/479a6860510286e69d9cb7ea3a448d5e) that you can use like this:

```typescript
<app-dialog 
  [component]="AnyComponent" 
  [open]="open$ | async"
></app-dialog>
``` 

GO TO THAT GIST AND COPY IT INTO YOUR CODEBASE RIGHT NOW. 

Stop living in pain. Enjoy declarative dialogs.

You should be proactive and wrap ALL imperative APIs in declarative APIs. 

## Other Imperative APIs in Angular

Dialogs are not the only place Angular has imperative APIs. We still have to write imperative code for component lifecycle hooks. Angular Reactive Forms should be called Angular Imperative Forms. There are others, too. I've written in the past about how to deal with these other imperative Angular APIs. Careful, it's a premium Medium article. [Here's the link](https://betterprogramming.pub/angular-isnt-reactive-enough-70b6b8d6cf8). 

## Side-Effects

Side-effects do not have to be be imperative. The entire DOM is technically a side-effect, but in Angular we (usually) write declarative templates for UI state. So why can't we handle all side-effects declaratively?

Dialogs are examples of APIs that end up outputting something to the user, but what about more behind-the-scenes APIs like `localStorage`?

For `localStorage`, reading state can be done synchronously, so that's not an issue when initializing state. The problem is when we need to push data into it because it has to be done imperatively with `localStorage.setItem()`. 

Rather than calling `setItem` in a callback function, we wish `localStorage` _itself_ could declare its own state over time. Something like this would be nice:

```typescript
this.localStorageService.connect('key', this.state$);
```

But what subscribes? What unsubscribes? And what if `state$` chains off of an `http$` observable? Do we want to trigger it immediately by subscribing? Clearly local storage should not be a primary subscriber to what it's watching. But RxJS does not support "secondary" subscribers, or passive listening of any kind. So, I see 2 possible solutions:

1. Tack on a `tap` to `state$`'s declaration. So everything that subscribes to 
    ```typescript
    state$ = defineStateSomehow().pipe(
      tap(s => localStorage.setItem('s', JSON.stringify(s))),
    );
    ```
   automatically triggers our callback function every time `state$` updates (if it has subscribers).

2. Create a wrapper component like we did for dialogs, so we can use it like this:
    ```html
    <app-local-storage
      key="key"
      [item]="state$ | async"
    ></app-local-storage>
    ```
    
    Is this weird? It kind of is. But it's so convenient. And if we want we can wrap that element in an `*ngIf` that controls when `app-local-storage` subscribes.

My thoughts are evolving on this, but #1 is still imperative, with that callback function passed into `tap()`. So I would personally prefer #2. But it might be a syntactic dead end that we'd have to undo if we encountered an unexpected scenario that needed more flexibility. 

Other imperative APIs can return observables, so they can be expressed reactively much more easily. For example, a POST request can be done like this:

```typescript
submit$ = new Subject<void>();

submissionSuccessful$ = this.submit$.pipe(
  withLatestFrom(this.form.valueChanges),
  concatMap(([, data]) => this.apiService.submit(data)),
);
```

Most of you are probably used to having a `submit` method instead. But that is imperative when it could be reactive. Why do you think `$http.post` returns an observable? Because POST requests return values, and it's not just so they can be lost in the depths of our app. We should probably have a wrapper for a toast component so we can show the user that their submission was successful:

```html
<app-toast
  [message]="submissionSuccessful$ | async"
  duration="3000"
></app-toast>
```

This is really nice. Hopefully Angular component libraries start providing declarative APIs for all of their components. 

## Summary

Imperative APIs are better than no APIs. We are grateful for developers who work on the difficult problems frameworks are solving. We are not surprised that the first APIs that solve problems turn out to be imperative. 

But we want to code declaratively. So, when we encounter an imperative API, our first instinct is to wrap it inside a declarative API. By doing this, we make it easier for our application code to stay clean and declarative as it grows in complexity.

![divider](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/46rby7k8kpgqa11khbbf.png)

---