I am working with AngularJS for my latest project. In the documentation and tutorials all model data is put into the controller scope. I understand that is has to be there to be available for the controller and thus within the corresponding views.
However I dont think the model should actually be implemented there. It might be complex and have private attributes for example. Furthermore one might want to reuse it in another context/app. Putting everything into the controller totally breaks MVC pattern.
The same holds true for the behaviour of any model. If I would use DCI architecture and separate behaviour from the data model, I would have to introduce additional objects to hold the behaviour. This would be done by introducing roles and contexts.
DCI == Data Collaboration Interaction
Of course model data and behaviour could be implemented with plain javascript objects or any "class" pattern. But what would be the AngularJS way to do it? Using services?
So it comes down to this question:
How do you implement models decoupled from the controller, following AngularJS best practices?
This article about models in AngularJS might help:
http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
I've tried to tackle that exact issue in this blog post.
Basically, the best home for data modeling is in services and factories. However, depending on how you retrieve your data and the complexity of the behaviors you need, there are lots of different ways to go about the implementation. Angular currently has no standard way or best practice.
The post covers three approaches, using $http, $resource, and Restangular.
Here's some example code for each, with a custom
getResult()
method on the Job model:Restangular (easy peasy):
$resource (slightly more convoluted):
$http (hardcore):
The blog post itself goes into more detail on the reasoning behind why you might use each approach, as well as code examples of how to use the models in your controllers:
AngularJS Data Models: $http VS $resource VS Restangular
There's a possibility Angular 2.0 will offer a more robust solution to data modeling that gets everyone on the same page.
You should use services if you want something usable by multiple controllers. Here's a simple contrived example:
I'm currently trying this pattern, which, although not DCI, provides a classical service / model decoupling (with services for talking to web services (aka model CRUD), and model defining the object properties and methods).
Note that i only use this pattern whenever the model object needs methods working on its own properties, that i'll probably use everywhere (such as improved getter/setters). I'm not advocating doing this for every service systematically.
EDIT: I used to think this pattern would go against the "Angular model is plain old javascript object" mantra, but it seems to me now that this pattern is perfectly fine.
EDIT (2): To be even clearer, I use a Model class only to factor simple getters / setters (e.g. : to be used in view templates). For big business logic, i recommend using separate service(s) that "know" about the model, but are kept separated from them, and only include business logic. Call it a "business expert" service layer if you want
service/ElementServices.js (notice how Element is injected in the declaration)
model/Element.js (using angularjs Factory, made for object creation)
DCI is a paradigm and as such there's no angularJS way of doing it, either the language support DCI or it doesn't. JS support DCI rather well if you are willing to use source transformation and with some drawbacks if you are not. Again DCI has no more to do with dependency injection than say a C# class has and is definitely not a service either. So the best way to do DCI with angulusJS is to do DCI the JS way, which is pretty close to how DCI is formulated in the first place. Unless you do source transformation, you will not be able to do it fully since the role methods will be part of the object even outside the context but that's generally the problem with method injection based DCI. If you look at fullOO.info the authoritative site for DCI you could have a look at the ruby implementations they also use method injection or you could have a look at here for more information on DCI. It's mostly with RUby examples but the DCI stuff is agnostic to that. One of the keys to DCI is that what the system does is separated from what the system is. So the data object are pretty dumb but once bound to a role in a context role methods make certain behaviour available. A role is simply an identifier, nothing more, an when accessing an object through that identifier then role methods are available. There's no role object/class. With method injection the scoping of role methods is not exactly as described but close. An example of a context in JS could be
The Angularjs documentation clearly states:
So it means that's up to you how to declare a model. It's a simple Javascript object.
I personally won't use Angular Services as they were meant to behave like singleton objects you can use, for example, to keep global states across your application.