The joy of loosely coupled Angular components
Tags: javascript, angular, tutorial
Published: 2019-07-02 14:18:43

![Framework Training angular components header puffins talking image](https://www.frameworktraining.co.uk/assets/components/phpthumbof/cache/framework-training-angular-javascript-tutorial-dev-to.7031f067f115e0b2dfc1100c7053a55d.jpg)

Our JavaScript guru John has put together a nice little example that looks at how Angular components that communicate in a __loosely coupled__ way can deal with change. This article looks at flexible approaches to __component communication__ and we hope it sparks your imagination about how you might apply these methods in your own applications.

## The DNA sequence application

* This small app creates sequences of the __four chemical bases in DNA__: adenine (A), guanine (G), cytosine (C), and thymine (T).

{% stackblitz angular-dna view=preview %}

* The data is an array of objects containing the four bases.

```javascript
const fourBases = [
    { name:"adenine", code:"A" },
    { name:"guanine", code:"G" },
    { name:"cytosine", code:"C" },
    { name:"thymine", code:"T" }
];
```

* The app contains two components, __base__ and __draw__ which need to communicate in some way.
* *My knowledge of the science of genetics is zero. The content is purely a vehicle for exploring Angular.*

## The base

* The __base component__ randomly picks a base once a second

```javascript
private base = null;

constructor() {
    interval( 1000 ).subscribe(n => this.setBase())
}

setBase() {
    let n = Math.floor(Math.random()*this.fourBases.length);
    this.base = this.fourBases[n];		
}
```

* The template displays the name of the selected base. 
* The __safe navigation operator__ (*the question-mark suffix in base?.name*) avoids a race-condition because property base is initially null.

```javascript
<!-- Example base: { name:"adenine", code:"A" } -->
<section>{{ base?.name }}</section>
```

## The sequence

* The draw component displays a sequence of bases, iterating over the array using an __ngFor directive__.

```javascript
<span *ngFor="let base of sequence" [ngClass]="base.name">{{ base.code }}</span>
```

* The __ngClass__ styling works because the CSS contains rules that match the base names.


```css
.adenine{  background-color: blue; }
.guanine{  background-color: sienna; }
.cytosine{ background-color: orangered; }
.thymine{  background-color: forestgreen; }
```

## Lets talk: Inputs & Outputs

* The two components need to communicate. 
* Every second the __base component__ picks a base which needs to be added to the sequence in the __draw component__.
* If the two components share a __common parent__ the default solution is to use __Inputs and Outputs__.
* The base component defines and outputs a __custom event__.

```javascript
@Output() changeBase: EventEmitter<any> = new EventEmitter();

this.changeBase.emit( this.base );
```

* The parent template __listens for changeBase events__ and calls a method in its own class to build the sequence.

```javascript
<base (changeBase)="buildSequence($event)"></base>	  
```

* The buildSequence method adds the base to its sequence. The ES6 spread operator pushes a *copy* of the object to avoid *copy-by-reference* bugs.

```javascript
  private sequence = [];

  buildSequence(base) {
    this.sequence.push({ ...base, code: base.code })
  }
```

* The parent template passes the sequence into the draw component as an input.
* The sequence array grows in length every second.

```javascript
<draw [sequence]="sequence"></draw>
```

* The draw component defines an Input.

```javascript
@Input() dnaSequence;
```

## Mediator

* The code works in a __loosely coupled__ way. 
* Base __outputs events__ without knowing who is listening.
* Draw __inputs__ a growing sequence without knowing who its' parent is.
* The two components share a common parent which acts as a __mediator__ passing data between them in its template.

```javascript
<base (changeBase)="buildSequence($event)"></base>
<draw [sequence]="sequence"></draw>
```

## Service with a smile 🙂

* But what happens when we __change__ the structure of the project? Without a __common parent__ this approach stops working.
* We *could* pass @Inputs down through __intermediate components__ which pass them on to their descendants. However this could become verbose and inflexible as the project grows.
* A better solution is to use a __service__ as the __mediator__ between the two components. 
* Services work in a loosely coupled way. Angulars' __dependency injection__ (DI) instantiates and passes a service into each component via its constructor. 
* Here is a first attempt at a service which creates a sequence of bases.

```javascript
export class MessageService {

  private sequence = [];


  addBase( base ) {
    this.sequence.push( {...base} );
  }

  getSequence() {
    return this.sequence;
  }
}
```

* However, this service does not provide a way of __notifying listeners__ that the sequence has changed.  


## Observable Subjects

* An Observable Subject solves this problem. The base component can add values to the stream. The draw component is notified when this happens
* This provides an ideal mechanism for __passing messages__ around components in a loosely coupled way. 

```javascript
export class MessageService {

  private channel = new Subject<string>();

  constructor() {}

  sendMessage(s: any) {
    this.channel.next(s);
  }

  getChannel() {
    return this.channel.asObservable()
  }
}
```

* The base component sends messages to the service.

```javascript
constructor(private ms:MessageService) {}

setBase() {
    this.ms.sendMessage( this.base );
}
```

* The draw component subscribes as a listener to the Observable stream and is notified of changes by the service .

```javascript
constructor(private ms:MessageService) {}

setBase() {
     this.ms.getChannel().subscribe( base => this.sequence.push({...base}))
}
```

* The service becomes the mediator between the two components.
* All the Input and Output code can be removed.
* The components do not need to share a common parent.
* The code continues to work even if the project is refactored into a different component hierarchy.


## Observable Clean-Up

* Observables are not part of the Angular core. If the draw component instance is destroyed, the subscribe code will continue running causing a __memory leak__.
* We need to store a reference to the Observable and then clean up in the __onDestroy__ lifecycle method.

```javascript
this.subs = this.ms.getChannel().subscribe( ....

ngOnDestroy() {
    this.subs.unsubscribe();
}
```

## Alternatives?

* This article has compared the relative merits of Inputs/Outputs against a Service as a mediator between components. Are there other approaches worth considering?

## Local Storage

* The browser provides __localStorage__ for reading and writing persistent domain-specific strings.
* We could share the sequence of bases between components using localStorage.

```javascript
// Base component
localStorage.bases = JSON.stringify( this.bases );

// Draw component
this.bases = localStorage.bases ? JSON.parse( localStorage.bases ) : [] ;
```

* However this approach does not make use of the mechanisms provided by the Angular framework for component communication. 
* And localStorage can be easily deleted/corrupted by the end-user in mid-application from the browser console.

```javascript
localStorage.bases = "rubbish";
```

## Redux

* Redux provides a store that the entire application can communicate with in a loosely coupled way.
* The base component could dispatch an action when it chooses a new base.

```javascript
export const BASE : string = "base" ;
export let setBase = ( b ) => ({ type: BASE, data: b }) ;
```

* A reducer function handles the action and updates the Redux store.
* The Redux store notifies listeners of any changes.
* The draw component includes code to listen for store changes.

```javascript
constructor(@Inject(BaseStore) private store:Redux.Store<Base>) {
this.store.subscribe(() => this.sequence = this.store.getState().sequence );
```
* Like the service mediator, this approach avoids the need to wire up Inputs and Outputs, and creates a loosely coupled architecture.
* Redux requires some additional setup and adds some complexity to the code.
* But it does provide a useful set of debugging tools via the Chrome Redux Extension.

## Summary

* Components are the fundamental building blocks of Angular applications. Defining clear channels of communication between components is an essential part of that process.
* My preference here would be to use a service as the mediator between two components. This provides a lightweight loosely coupled solution that will continue to work as the project changes.

## A bit about us

[Framework Training Ltd](https://www.frameworktraining.co.uk) is a UK-based provider of face-to-face technical training. We have a fantastic family of domain experts, with coverage of key disciplines including software development in languages including JavaScript, C#, Python, Scala et al. We also specialise in  DevOps platforms and techniques, Data Science, Agile methods and secure application development too. Here's our [Angular training course](https://www.frameworktraining.co.uk/courses/coding/javascript-js-libraries-frameworks/angular-training-course) outline to give you an idea of what we do. 

We would love to know what you think about this tutorial and would welcome your questions.