We all know that Angular follows MV* design pattern and in
this pattern M stands for Model. But where is the Model in AngulaJS. In all the
demos and samples we see the model mysteriously disappeared. Some people claim
that $scope is the model. This may not be correct on how you look at the model.
Before going further let’s check the definition of model.
Here is the definition from Microsoft on model
Model.
The model manages the behavior and data of the application domain, responds to
requests for information about its state (usually from the view), and responds
to instructions to change state (usually from the controller).
So a model should include a stateful object along with
business and validation logic. Most of the times model also includes the data
access layer. In case of Angular the data access layer is a RESTful call. Currently
in most of the apps controller is doing the model’s responsibility. Most
controllers persist the state (sometimes in $scope) and these controllers
perform the data fetching and validation. This is ok for a trivial demo or for
small applications. But for complicated or large applications we need clear
separation of concerns and hence cannot convolute controllers with the model
functionality.
If you see ASP.NET MVC, the model includes only a stateful
POCO object and validations. As per the design pattern this is may not appear
as the true implementation of model. Our model should include business and data
access. In large ASP.NET MVC projects we typically have business and data
layers. Hence our model actually consists of a stateful object with business
and data layers. So our model was split into multiple files, a stateful object
and a business object. Let’s extend the same to AngularJS
Putting “M” back in
Angular
Similar to ASP.NET MVC let’s split our model into two files,
one contains the stateful object and another a service which acts as business
and data layer. In our next section we will move the stateful object to the
right place where it belongs.
Let’s assume that we are working on a project where we are
using contacts. With that premise let’s create a stateful object called
contactModel.js with the following code
'use
strict'
angular.module('ngmodels').value("contactModel",
contactModel());
function contactModel() {
return {
ContactID: 21,
FirstName: 'Praval',
LastName: 'Pattam'
};
}
In the above code we defined an angular value that contains
a model with ID, FirstName and LastName properties. Next let’s define an
angular service that performs business rules. Here is the sample code:
'use
strict'
angular.module('ngmodels').factory('contactService',
contactService);
contactService.$inject
= ['contactModel'];
function contactService(contactModel) {
return {
get: get,
save: save
}
function get() {
//fetching
the contacts
return contactModel
}
function save() {
//saving
the contacts
}
}
As you see our contactModel and contactService together form
the Model. With this approach we can use this model in our controller. Let’s
see how our controller looks like:
'use
strict';
angular.module('ngmodels').controller('contact', contact);
contact.$inject
= ['contactService'];
function contact(contactService) {
var vm = {
model: {},
save: save
};
initialize();
return vm;
function initialize() {
vm.model = contactService.get();
}
function save() {
contactService.save(vm.model);
}
}
As you see in the above code we are injecting the
contactService (which is part of our model) and our controller passes this
model to the view using viewmodel pattern.
Now with our Model we were able to clearly separate the
layers and made our controller clean.
In most of the applications we bring the data from the
server using a RESTful call. The web server fetches the data from database, executes
business logic and returns it as a JSON object. So in our server we typically
define a contact object that holds the contact data and sends it back. Here is
the definition of the Contact class in C# that is returned to Angular using
WebAPI
public class ContactModel
{
public int ContactID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
When this object is returned as JSON, JavaScript
automatically creates a stateful object. This will remove the need of defining
the contactModel in javascript. With the server side model, our contactService
will be changed to use resources to fetch the model from server using the below
code.
'use
strict'
angular.module('ngmodels').factory('contactService',
contactService);
contactService.$inject
= ['$resource'];
function contactService($resource) {
return $resource(
"/api/contact"
);
}
So sometimes it is argued that in Angular the model resides
on the server. The client uses that server model in the view. But as explained
earlier, model contains two pieces. In Angular the definition of the stateful
model resides on the server and some part of the data and business resides on
the client. Hence the model resides in two layers. It’s funny as it sounds but
that is correct.
As you have seen Model is a complex piece in Angular.
Your example of model is as trivial as the demos you are pointing to. If there's no business logic inside the model why have them? Even if there's business logic why would someone not prefer to keep it on the server side away from prying eyes, instead of keeping it on the JS easily visible using browser developer tools? How different will your server side model be from the client side one? Yes you can put models, DTO and what not in each layer in the name of separation of concerns and made it a very complex, unmaintainable and ugly application. Best of luck!
ReplyDeleteThanks for your comment. My objective of this blog is to describe where the Model in an Angular app. I am not talking which is the best approach. A typical answer for the best approach is "it depends". In some mission critical applications you have to keep your business logic on the server while in others you can keep it on the javascript side.
ReplyDeleteI agree with you that we should not do layers just for the sake of it. If our application needs it and it is justifiable for the complexity and overhead then we should definitely go for the separation.
Yes, i agree with your point. $scope object cannot be the model. In Angular Model is just a JavaScript object.
ReplyDeleteAngularjs Training in Chennai
One of the simplest and the smartest solutions for Angular model.
ReplyDeleteVery cool and grammotno article, especially how avtror clarifies the meaning of "model" and then demonstrates the clear concept of the script. Very informative and interesting! Thank you for sharing
ReplyDeleteRichard Brown virtual data rooms