View Pattern - Angular design pattern 
Tags: angular, typescript, designpattern
Published: 2021-01-25 00:00:33

View Pattern is a front-end design pattern. The View Pattern is a way to automatically inject a view corresponding to the asynchronous request state. For example, a component that depends on a data retrieved by an HTTP request will start with a loading state then depends on the resolved state (error or success) it will switch to main or the error view.

If you are a web front-end developer you may recognize a repetitively UI pattern of displaying a loader while an asynchronous request is being processed, then switching do the main view or displaying and error. Personally, I noticed that in single page applications with multiple components per page that being loaded independently I have a repetitive code. And even worst, is the fact that I am not getting any indication for missing templates (if I forgot to implement an error handler or a loader).

In this short article I am going to share my "View Pattern" solution attempting to reduce code duplication and notify for missing parts.

### The `View` in "View Pattern"
To achieve the goal of a reusable view pattern we need to start by defining an interface to store the `View` state. This view state can be of any complexity, but for this article I will focused the following states:
**Loading** - the state before the asynchronous request has been resolved. This state will inject the `Loader` template. 
**Data** - upon a (successful) response the display data will be mapped into the `main` template.
**Error** - if the request failed the error state will contain the reason for the failure and instructions for the `error` template display.

```typescript
export class View<T> {
  data?: T; // Store component data (of generic type T)
  loader?: boolean; // Whether to show the loader
  error?: Error;
}
```

In this specific implementation I am going to use [RxJS Observables](https://rxjs-dev.firebaseapp.com/guide/observable) to handle the async events and manipulate the flow.
For every event coming out our main event emitter we will wrap it in a `View` state. Since http is the most used observable we will use it for our example: `const view$: Observable<View<T>> = this.httpClient<T>(<url>)`. 
We will `startWith` emitting the loading state. Then when we receive the response event (with the data `T`) we will `map` it to `View<T>`. For handling errors we will add `catchError`.
```typescript
const request$: Observable<View<T>> = this.httpClient<T>(<url>).pipe(
  startWith({loader: true}),
  map(response => ({data: response})),
  catchError(error => of({error})));
```
**Note:**
* `T` is a placeholder for the response type

### "View Pattern" viewContainer
The `ViewContainer` is responsible to inject the correct template for a given view. In this tutorial we will use a structural directive as an example.

The usage will look as follows:
```html
<div *viewContainer="view$ | async;
                          main mainTmp;
                          error errorTmp;
                          loading loaderTmp">
<div>

<ng-template #mainTmp>...</ng-template>
<ng-template #errorTmp>...</ng-template>
<ng-template #loaderTmp>...</ng-template>
```
```html
<view-container
  *ngIf="view$ | async as view"
  [appViewMain]="mainTmp"
  [errorTmp]="errorTmp"
  [loaderTmp]="loaderTmp"
  [view]="view">
</view-container>

<ng-template #mainTmp>...</ng-template>
<ng-template #errorTmp>...</ng-template>
<ng-template #loaderTmp>...</ng-template>
```
In the next part we are going to implement that structural directive. But, it could also be a component. If you are interested you can find [full implementations](https://github.com/natanbr/view-pattern-angular/blob/master/src/app/models/view.ts).

### `viewContainer` Implementation
First let's create our Directive
```Typescript
@Directive({ selector: '[viewContainer]' })
export class ViewContainerDirective<T> implements AfterViewInit {

   ngAfterViewInit(): void {
       // Verify all the templates defined, throw an error otherwise 
   }
}
```

Next, define the properties to save the reference templates
```Typescript
  private _mainTemplateRef: TemplateRef<AppViewContext<T>> = null;
  private _errorTemplateRef: TemplateRef<AppViewContext<T>> = null;
  private _loaderTemplateRef: TemplateRef<AppViewContext<T>> = null;
```
and bind the template reference (`#<name>`) to the properties. 
```Typescript
@Input() set viewContainerMain(templateRef: TemplateRef<any>) {
    this._mainTemplateRef = templateRef;
}

@Input() set viewContainerError(templateRef: TemplateRef<any>) {
    this._errorTemplateRef = templateRef;
}

@Input() set viewContainerLoading(templateRef: TemplateRef<any>) {
    this._loaderTemplateRef = templateRef;
}

```
In case you wonder how that binding works check the [microsyntax for directives](https://angular.io/guide/structural-directives#microsyntax). In short, the setter name is a combination of the directive name (prefix) with the attribute name (suffix).

Now, let's go back to the `ngAfterViewInit` and add the check if one of the templates is missing
```Typescript
  ngAfterViewInit(): void {
    if (!this._errorTemplateRef) throw new Error('View Pattern: Missing Error Template')
    if (!this._loaderTemplateRef) throw new Error('View Pattern: Missing Loader Template')
    if (!this._mainTemplateRef) throw new Error('View Pattern: Missing Main Template')
  }
```

Finally, each time the `View` is changed insert the template to the container. For that we can use `createEmbeddedView` [API](https://angular.io/api/core/ViewContainerRef#createembeddedview) So let's inject the `ViewContainerRef` Service.
```Typescript
constructor(private _viewContainer: ViewContainerRef) { }
```
One of `createEmbeddedView` optional parameters is a context. Providing the context will allow accessing the data (`T` - the one from the `View<T>`).
```Typescript
private _context: AppViewContext<T> = new AppViewContext<T>();
```
Now, we have everything we need to implement the setter:
```Typescript
@Input() set viewContainer(view: View<T>) {
    if (!view) return;

    this._context.$implicit = view; // setting view to be avilable in the template
    this._viewContainer.clear(); // Clears the old template before setting the the new one.

    if (view.loader)
      this._viewContainer.createEmbeddedView(this._loaderTemplateRef, this._context);

    if (view.error && !view.loader) // Defines the conditions to display each template in single place
      this._viewContainer.createEmbeddedView(this._errorTemplateRef, this._context);

    if (view.data && !view.error) 
      this._viewContainer.createEmbeddedView(this._mainTemplateRef, this._context);
  }
```


### Wrapping Up
In this tutorial we implemented the "view pattern" allowing us to simplify our components by reducing duplicated code, flattening the templates. While at the same time reducing the chances for potential bugs by getting some feedback when something is missing.
That pattern can be easily extended to support more complicated states, and it will support "skeleton loaders" out of the box by providing both the `mock data` and the `loader`. [You can check the full code and examples on Github](https://github.com/natanbr/view-pattern-angular/blob/master/src/app/models/view.ts).