A word of caution, don’t get fooled. Even though this guide starts rather simply with things you know and take for granted; that’s just an introduction or preparation of a context if you will… It soon progresses to hardcore ES6 goodness, you are searching for, so no worries!

Angular before models

Most of the old Angular JS tutorials on the internet (and also projects you encounter while working for the clients) are still using simple approach where the $http service is used directly in the controller and the data is then unwrapped and assigned to some $scope property so that it is usable in the html template.

Storing data in controller’s $scope doesn't belong between recommended practices for some time now. Using controller/component itself (binding data to some property on controller’s this) is virtually the same solution as with $scope besides skipping soon to be deprecated API (yes, the $scope itself).

The protomodel

Some articles (e.g.: this & this) have proposed that $http request should be resolved directly in the service and then stored in some exposed (public) service’s variable. Service is then injected into controller and services public variable which stores resolved data is then bound to controller’s this (or $scope). After that we can access data in the template.

Those guides usually used angular.copy(retrievedData, model) to prevent breaking of the controller’s reference to the model when updating the model’s data. Following code snippet illustrates the situation.

The “protomodel”

So far so good… But can we improve and standardize this pattern so that it incorporates all the modern JavaScript goodness while simplifying the effort of dealing with bigger projects?

Trust me on this one… Standardization is the best thing ever ™ when you have to collaborate with multiple developers on the same project

The Model

Syntax used in the following examples

In this post (and also on projects @mimacom) I am using ES6 syntax (mostly classes, arrow functions, default parameters etc…) with Babel (formerly 6to5) transpiler. I prefer Babel because it produces code that looks more like original code in comparison to output produced by traceur.

Simple model

We will use approaches described in “protomodel” but we will adjust & standardize model’s structure.

Primary purpose of models is to perform CRUD operations on the data. Models can hold data of single item or collection of these items (corresponding to REST’s GET performed on /resource root vs get /resource/{resourceId}). Besides that we want to persist new or update (delete) existing items.

If we only needed CRUD operations it would be enough to just use $http service (mentioned above) but we often need to implement specific business logic and calculations too. As it turns out models are great place to do that. Having data and functionality that process that data located in one place (file) adds to readability and maintainability.

Fetching the data

We implement models in application which uses ui-router to define its states. Every state definition can contain a resolve property. While performing state transition, ui-router waits for successful execution of every function defined in resolve (resolving of all promises) before transition is completed and user is presented with the new UI. This is the place where we want to initialize our models so that the data is ready to be used in our components.

If you are asking what’s the meaning of <div my-component></div> you can check the article about Component pattern for Angular JS 1.X.

As you may have noticed we’re passing itemId to the SomeOtherModel intiItem() method. This is because it implements 2 use cases:

Get data for specific item (identified by itemId) from server Initialize new item when called without itemId (which may or may not be later persisted on server)

Similarly to “protomodel” we resolve and store data in model’s properties instead of passing them directly into the controller. We call these properties item and collection.

It is possible that some models will have only item or only collection or even multiple different item properties, based on specific model’s needs

Using model in component

Now it’s the time to use our model in some component. We do that by binding the model directly on our component’s this.

Component is actually a classic Angular JS controller written as a ES6 class which can be used in directive or directly in ui-router state definition. Don’t forget to always use it with controllerAs, and in case of directive also with bindToController properties

At last, we can access model’s data and functionality in component’s template.

We don’t have to take special precautions to prevent breaking of the references to the data as in case of the “protomodel” (no need to employ angular.copy()). Model is bound directly to component and because it is implemented as Angular’s service (all Angular services are singletons) the reference will point to the same service during whole application’s lifecycle.

To put it all together, lets see how an implementation of a concrete model can look like, but before we start I should clarify some things used in example…

Infrastructure related services used in model

BookRestResource is service which contains definitions of all available REST calls for Book resource (e.g.: standard CRUD, but possibly also custom methods for back-end validation or manipulation of sub-resources). Service internally uses angular’s $http service.

Use $http service only in *RestResource services to ensure correct, consistent and predictable responsibility separation.

PromiseService is utility service used for creating and resolving (or rejecting) of helper promises. We use it to return promises from model’s methods in case we don’t do any async REST call because they are expected in controller. (You can use $q manually if you like, I am just to lazy to write yet another var deferred = $q.defer())

httpInterceptor is Angular’s interceptor used together with $http service to perform specified operations on every request and response (or rejection). Our httpInterceptor takes care of unwrapping of successful responses centrally so we don’t have to call response.data manually (also you can register multiple httpInterceptor-s if needed).

Sharing one model in multiple components

Lets imagine situation that we’re implementing some e-commerce solution where you can order / buy multiple items. In this case we usually have some orders overview component with table of already ordered items, their prices, total price and a checkout button. We also want a basket component as a part of general layout (for example in header) to get quick overview of how many items we already ordered while browsing other products.

Both specified components display the same data but in a different way. Overview shows rich details about the placed orders while basket may just show orders count and possibly also total price.

This use case is solved automatically by using models. It is enough to just inject OrderModel to both components and they will automatically display correct synchronized data independently from how that data changed just by virtue of how angular’s two way data binding works.

Name-spaced model

So far we just used model of one type (e.g.: BookModel) in one or multiple components simultaneously to get the benefits of automatically synchronized data in multiple views.

What if we have to implement use case where we need two unique instances of book item (e.g.: show and edit two different books side by side ). Both books should be stored in item property of a BookModel but our model allow us to store only one item at one time and it is singleton so we can’t create multiple instances of the model itself.

This can be solved by employing name-spaced models. They are virtualy the same as above described models just with one key difference. We store items and collections in their respective namespace of this.models property and every defined method also accepts optional modelId as a last parameter.

Instead of data being stored directly in this.item and this.collection we provided a this.models object with one model initialized by default (main). We use getModel(modelId = ‘main’) with default parameter ‘main’ in other functions to perform operation on desired model (as used in initCollection()).

We also provided two getters (for item and collection) so that we can access main model without need to explicitly reference it in our component or template. We can use ctrl.NameSpacedModel.collection instead of ctrl.NameSpacedModel.models.main.collection.

By employing this approach, our name-spaced model behaves exactly the same as normal model would. It just provides us optional possibility to define and manipulate models in other namespaces too.

You should always consider your particular use case when deciding which implementation would be more suitable for your particular situation because name-spaced models are inherently more complex

Non-model service

Models usually contain implementation of some related business logic. Because of this, it may be tempting to inject models into each other to access this functionality. While this approach may be sensible if we are in situation that one Model is sub-model of another (e.g.: Book and Chapter models), it is generally not a good idea.

If we need functionality of both models simultaneously it is better to create some specific service which then injects both models and orchestrate their interaction. Again, we achieve clearer separation of concerns and make our code (models) more reusable because they will not implement some very specific edge cases themselves.

Conclusion

It is always good to define and follow patterns when developing some bigger than TodoMVC project. I already used “protomodel” and “Model pattern” in two larger professional projects.

Disadvantage of need to invest a bit more time into planing on how to structure your models is out-weighted by the benefits of eliminated need to explicitly synchronize your data (no more $broadcast-s and $watch-es) because all components using the same model have access to same shared data automatically (two-way data binding). Also following one standard pattern increases productivity of multiple developers in one project just because the code always feels familiar (independently from who wrote it) which is often not the case when developing JavaScript application.

If you are interested in more front end related stuff then check my article: Guide to simple, parametrized Webpack builds and don’t forget to recommend this article if you really liked it.

Follow me on Twitter to get notified about the newest blog posts and useful front-end stuff.