> ## Content Index
> Fetch the complete content index at: https://www.angularspace.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# VM$ pattern in Angular
- URL: https://www.angularspace.com/vm-pattern-in-angular/
- Published: 2024-07-17T05:30:20.000Z
- Updated: 2024-07-17T05:30:20.000Z
- Author: Adam Wolak
- Tags: Articles

Hello Angular Space Community!

As an Angular developer, you may face issues when your component has to use multiple asynchronous sources. Beginner Angular developers may encounter some minor problems with that and might not solve them correctly, so I want to show you a simple trick.

### Case Study - Handle multiple Observables inside component

Angular emphasize to write reactive code with Observable/Promise - HTTP Requests, `valueChanges` in FormControl, `navigate` method in Router - even third-party libraries using this methodology, like `@ngrx/store`, where you can access to state using selectors which are Observables.

Let's prepare example component where we want to display information from multiple sources.

```ts

@Component({
  selector: 'app-pseudo-page',
  standalone: true,
  imports: [AsyncPipe],
  templateUrl: './pseudo-page.component.html',
  styleUrl: './pseudo-page.component.css',
})
export class PseudoPageComponent {
  private readonly todoService = inject(TodoListService);
  private currentPageSubject = new BehaviorSubject<number>(1);
  readonly currentPage$ = this.currentPageSubject.asObservable();
  private readonly currentUser$ = inject(CurrentUserService).currentUser$;

  readonly loggedTime$ = interval(1000);
  readonly todoList$ = this.currentPage$.pipe(
    switchMap((page) => this.todoService.getTodos(page, 10))
  );

  prevPage() {
    this.currentPageSubject.next(
      this.currentPageSubject.value - 1 < 1
        ? 1
        : this.currentPageSubject.value - 1
    );
  }
  nextPage() {
    this.currentPageSubject.next(
      this.currentPageSubject.value + 1 > 10
        ? 10
        : this.currentPageSubject.value + 1
    );
  }

```

To display them on the template we use `Async` pipe - this one makes subscription by their own, will update the template when data will be changed and unsubscribe it when the component will be destroyed. We don't want to manage subscription manually.

Let's apply data in template. For this one, we use trick `async + as XYZ` inside `@if`.

```html
<div class="pseudo-page__header">
  <h3>Hello {{ currentUser$ | async }} !</h3>
  <h3>You are logged for {{ (loggedTime$ | async) || 0 }} s</h3>
</div>
<div class="pseudo-page__container">
  <div class="pseudo-page__todo-list">
    <h2>There is your todo-list</h2>
    <ul>
      @for (element of (todoList$ | async); track element.id) {
        <li>{{ element.title }}</li>
      }
    </ul>
    <div class="pseudo-page__actions">
      <button class="pseudo-page__button" (click)="prevPage();" [disabled]="((currentPage$ | async)  || 1) <= 1">Prev</button>
      <div>{{currentPage$ | async}}</div>
      <button class="pseudo-page__button" (click)="nextPage();" [disabled]="((currentPage$ | async) || 1) >= 10">Next</button>
    </div>
  </div>
</div>

```

At first sight, the component code looks acceptable, but the template starts to look a little bit messy. We need to notice, even if `async` is our friend in this case, each of them is a different subscription, which may cause huge issues with performance and code management.

To improve that, we can implement *ViewModel* pattern.

### View-Model in Angular

*View Model* is a part of MVVM pattern (Model-View-ViewModel) and it's about seperate Model (M) from View (V) and create some kind of bridge to connect them - View Model (VM). In other words - ViewModel is an object, which (with Model's helps) decide which data will be passed into view.

Using Angular parts we can explain this Architecture like:

- Model is Component and all data what we have there
- View is Components's template
- View-Model is component property, which we will use inside template

![](https://storage.ghost.io/c/3f/63/3f6333b8-1f83-4017-8426-91bfbc264d81/content/images/2024/07/mvvm.png)

### Practical example

With this knowledge, let's try to refactor our code.

First - let's prepare variable, which we pass into our template. To keep the naming standard - let's call it `vm$`. We use here functions [combinateLatest](https://rxjs.dev/api/index/function/combineLatest?ref=angularspace.com#combinelatest) operator and [map](https://rxjs.dev/api/index/function/map?ref=angularspace.com). The first one will be used as combination of our all sources, the second to change value of each stream into one object.

```ts
  readonly vm$ = combineLatest([
    this.loggedTime$,
    this.currentPage$,
    this.currentUserService.currentUser$,
    this.todoList$,
  ]).pipe(
    map(([loggedTime, currentPage, currentUser, todoList]) => {
      return {
        loggedTime,
        currentPage,
        currentUser,
        todoList
      }
    })

```

And that's all we need to do inside our component. And now we need to pass this variable to our template just like we already did, but instead of multiple `async` pipe, we have only one, with all data which we're interested in. Because `vm$` does refer to whole component's data - it's good to put at the top of the component template.

```html

@if (vm$ | async; as vm) {
  <div class="pseudo-page__header">
    <h3>Hello {{ vm.currentUser }} !</h3>
    <h3>You are logged for {{ vm.loggedTime }} s</h3>
  </div>
  <div class="pseudo-page__container">
    <div class="pseudo-page__todo-list">
      <h2>There is your todo-list</h2>
      <ul>
        @for (element of vm.todoList; track element.id) {
          <li>{{ element.title }}</li>
        }
      </ul>
      <div class="pseudo-page__actions">
        <button class="pseudo-page__button" (click)="prevPage();" [disabled]="((vm.currentPage)  || 1) <= 1">Prev</button>
        <div>{{vm.currentPage }}</div>
        <button class="pseudo-page__button" (click)="nextPage();" [disabled]="((vm.currentPage) || 1) >= 10">Next</button>
      </div>
    </div>
  </div>
}

```

And that's it, from now on, the component will be updated every time when one of sources from `combinateLatest` emit a new value. Even if you set `OnPush` change detection strategy in this component, you will be almost sure that your component will be rerendered without calling `detectChanges()` from `ChangeDetectorRef`. `Async` pipe is one of few solutions prepared from Angular team where when in changes your component is flagged for update.

This pattern learn you and your team how to separate things for component and for template. As you can see - the template know, which function from component it may call, it has access to some data - "some" data, not all of them. Additionally we can manipulate this streams values with other operators, like `switchMap`, `delay`, `map` etc.

```ts
  readonly vm$ = combineLatest([
    // our source is "interval" but we want to display in template nice formated time m:ss
    this.loggedTime$.pipe(
      map((time) => {
        const minutes = Math.floor(time / 60);
        const seconds = time - minutes * 60;
        return [minutes, ('0' + seconds).slice(-2)];
      }),
      map((time) => time.join(':')),
      startWith(0)
    ),
    this.currentPage$,
    this.currentUserService.currentUser$,
    this.todoList$.pipe(startWith([])),
  ]).pipe(
    map(([loggedTime, currentPage, currentUser, todoList]) => {
      return {
        loggedTime,
        currentPage,
        currentUser,
        todoList,
      };
    })
  );

```

### Hold on, I think I already saw it...

Yup, this pattern is recommended when you use `@ngrx/store` and `@ngrx/component-store`. Store is asynchronous, selectors returns Observable with data, so in case when you have to use multiple selectors from store in one component - using `vm$` is recommended.

### No Control-Flow in template? No problem!

You may ask - "How can I use it in my application? I still use the oldest version of Angular and I don't have control-flow in my templates..." and I'm happy to tell you that it is the pattern - It's not require any special libraries or specific Angular version , we can change tools to achieve our goals, so you can still use it!

As you already know, before control-flow (`@if`, `@for` etc) we use same "structural directives" like `*ngIf`, `*ngFor` etc. You can use them for creating subscription in your template (just like we already do with `@if`) on `<ng-container/>` or other element.

```html
<ng-container *ngIf="(vm$ | async) as vm">
    <!-- Access to your data as vm.SOMETHING -->
     <div class="pseudo-page__header">
        <h3>Hello {{ vm.currentUser }} !</h3>
        <h3>You are logged for {{ vm.loggedTime }} s</h3>
    </div>
    ...
</ng-container>

```

I feel obligated to mention that there are some prepared solutions dedicated mainly for managing asynchronous values/state in template like for ex. [RxLet directive from @rx-angular/template package](https://www.rx-angular.io/docs/template/api/rx-let-directive?ref=angularspace.com), which solve some issues and do improvements compare witth @if/\*ngIf.

## What's next? `@let` instead of `@if`

Noteworthy is fact that Angular 18.1 will introduce the [@let syntax](https://github.com/angular/angular/pull/55848?ref=angularspace.com). This will allow you declare variables directly inside component template. Using new syntax, you can remove `@if` block and create directly vm property

```html
@let vm = (vm$ | async);
<!-- Access to your data as vm.SOMETHING  -->
 <div class="pseudo-page__header">
    <h3>Hello {{ vm.currentUser }} !</h3>
    <h3>You are logged for {{ vm.loggedTime }} s</h3>
</div>

```

### Conclusion

The solution I just described above (`@if` \+ `async` \+ `combinateLatest`) is probably the most popular and universal, but sometimes additional modification will be required.

- `combinateLatest` emit value when every sources emits their own. If one of them didn't emit value, `combinateLatest` will wait for them. In order to fix it, you can try to use `startWith` operator on source.
- Using `@if` to assign property from `async` may cause some unexpected issues. If whole `vm` will be "falsy value" (0, false, null etc) this directive will not render the component.

In this article I wanted to show you this technique. I use them every day and - for me - this is the base of my work to create "smart components".

Example code which I use in this article is available in [StackBlitz](https://stackblitz.com/edit/stackblitz-starters-zwxfv3?file=src%2Fpseudo-page%2Fpseudo-page.component.ts&ref=angularspace.com), feel free to play with it!

---

![](https://storage.ghost.io/c/3f/63/3f6333b8-1f83-4017-8426-91bfbc264d81/content/images/2024/07/Screenshot-2024-07-17-at-02.17.33.png)