Applying a MVVM-like pattern in Angular (Part 1)
In this article I will try to cover a successful approach I had, in a large scale Angular app, applying a MVVM-like pattern. We will go through it in 2 parts. In the first section we’ll take a look at a basic and easy setup and in the second part we’ll apply it in more advanced scenarios.
What are our goals?
- achieve a complete separation between component and business logic by applying MVVM
- make the business layer easily extendable
- make the business layer easily interchangeable
- make the business layer easily testable
- keep the UI stuff exclusively in the component
- make this pattern applicable for any component
Before diving in…
I am assuming at this point that you are familiar with Angular and basic Angular concepts like Components and Services (or Injectables if you prefer).
However, it would also help to get a bit familiar with MVVM and what it stands for. I uncovered it years ago while working with C# & WPF through a great article that is still available today. Check it out: https://www.codeproject.com/Articles/819294/WPF-MVVM-step-by-step-Basics-to-Advance-Level
Here we go:
As a concept, the entire component code will revolve around what we’ll call the view-model from this point on. The purpose of this structure will be to encapsulate not only the business object (which may or may not come from a backend) but also any UI-related fields like button visibilities, conditions, labels, selected values and other. Whether or not you separate the main business object from the view-model is up to you, but we’ll keep them together.
Another essential part is to have a separate service that handles all business logic behind the component. This service, which we’ll call ui-service, will only manipulate the view-model and through the magic of Angular binding the UI will automatically update. This means that most, if not all bindings from our html view will point to our view-model instance. We must share the same instance between all the parts of our pattern.
We will use a variation of the hero example from the Angular site to apply this pattern.
Ok, let’s first take a first look at how our component structure looks like:
You might notice that besides our hero-component folder we also have a abstract folder with a couple of files. While this is not entirely necessary to implement this pattern, they do become useful over time when applying it to many components. So, just go ahead and implement them. They are not large nor complicated.
As for the component folder, we notice the classical html+scss+ts pattern that we are familiar with in Angular. The two extra structures we see are the view-model and the ui-service.
We’ll take the files one by one and detail them.
(Abstract) ui-service.ts
A simple support class for retaining & creating the view-model instance. For now, we assume that all of our view-models will be defined as interfaces.
(Abstract) ui-component.ts
A very basic & simple abstract class that all of our components, using this pattern, should extend. The contents are as follows:
That’s it for the abstract classes. Let’s now look at our component files.
hero.comp.view-model.ts
hero.comp.ui-service.ts
And the accompanying spec file hero.comp.ui-service.spec.ts
hero.comp.ts
And finally, our hero.comp.html
And that’s our initial setup. We can now run our app and display our component. We can manipulate the model without ever leaving the ui-service. We can fetch data from a server or simply do computations. We can inject other services and use them. Just remember to keep things apart: business requirements get implemented in ui-service; UI-related requirements get implemented in the component. And remember, you can add your business object to the view-model if you need to and have the same level of access as you’d have with any other field.
Interacting with our ui-service
Our job is not done. We still want to know when something happens in our ui (a button is pressed or something is checked) and notify the ui-service about the event.
The solution we will apply for now is a very simple one: use the component to listen for the events and just call the methods on the ui-service.
Let’s add some buttons to our html:
And add the methods to our component class:
And finally, to our ui-service:
Done. I know, this could have been easily done directly in the html, and you can still do that. But this is a proof of concept and we wanted to let the ui-service know that something changed. Of course, you can add parameters to the methods and so on and even use 2 way binding on the view-model fields.
This concludes Part 1 of this 2 part article. I know it doesn’t seem like something out of the ordinary and it truly isn’t. Try imagining you have to implement a very large & complicated business logic behind this component along with animations, ui adjustments, resize handlers and so on. It has truly helped me manage such code by keeping the separation between the two layers clear & clean.
The view-model itself is extendable and you can add any kind of state information you need. It is not necessary to use it exclusively in the html, you can just keep it as part of the component state so you also save some space by not declaring lots of global fields & variables.
In Part 2 of this guide, we will try to enhance our pattern a bit to make it even more flexible and we’ll see how we can have multiple ui-services and make them interchangeable based on a context.