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?
You should use services if you want something usable by multiple controllers. Here's a simple contrived example:
myApp.factory('ListService', function() {
var ListService = {};
var list = [];
ListService.getItem = function(index) { return list[index]; }
ListService.addItem = function(item) { list.push(item); }
ListService.removeItem = function(item) { list.splice(list.indexOf(item), 1) }
ListService.size = function() { return list.length; }
return ListService;
});
function Ctrl1($scope, ListService) {
//Can add/remove/get items from shared list
}
function Ctrl2($scope, ListService) {
//Can add/remove/get items from shared list
}
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)
MyApp.service('ElementServices', function($http, $q, Element)
{
this.getById = function(id)
{
return $http.get('/element/' + id).then(
function(response)
{
//this is where the Element model is used
return new Element(response.data);
},
function(response)
{
return $q.reject(response.data.error);
}
);
};
... other CRUD methods
}
model/Element.js (using angularjs Factory, made for object creation)
MyApp.factory('Element', function()
{
var Element = function(data) {
//set defaults properties and functions
angular.extend(this, {
id:null,
collection1:[],
collection2:[],
status:'NEW',
//... other properties
//dummy isNew function that would work on two properties to harden code
isNew:function(){
return (this.status=='NEW' || this.id == null);
}
});
angular.extend(this, data);
};
return Element;
});
The Angularjs documentation clearly states:
Unlike many other frameworks Angular makes no restrictions or
requirements on the model. There are no classes to inherit from or
special accessor methods for accessing or changing the model. The
model can be primitive, object hash, or a full object Type. In short
the model is a plain JavaScript object.
— AngularJS Developer Guide - V1.5 Concepts - Model
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.
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
function transfer(source,destination){
source.transfer = function(amount){
source.withdraw(amount);
source.log("withdrew " + amount);
destination.receive(amount);
};
destination.receive = function(amount){
destination.deposit(amount);
destination.log("deposited " + amount);
};
this.transfer = function(amount){
source.transfer(amount);
};
}
As stated by other posters, Angular provides no out-of-the-box base class for modeling, but one can usefully provide several functions:
Methods for interacting with a RESTful API and creating new objects
Establishing relationships between models
Validating data before persisting to the backend; also useful for displaying real-time errors
Caching and lazy-loading to keep from making wasteful HTTP requests
State machine hooks (before/after save, update, create, new, etc)
One library that does all of these things well is ngActiveResource (https://github.com/FacultyCreative/ngActiveResource). Full disclosure--I wrote this library--and I have used it successfully in building several enterprise-scale applications. It's well tested, and provides an API that should be familiar to Rails developers.
My team and I continue to actively develop this library, and I'd love to see more Angular developers contribute to it and battle test it.
An older question, but I think the topic is more relevant than ever given the new direction of Angular 2.0. I would say a best practice is to write code with as few dependencies on a particular framework as possible. Only use the framework specific parts where it adds direct value.
Currently it seems like the Angular service is one of the few concepts that will make it to the next generation of Angular, so it's probably smart to follow the general guideline of moving all logic to services. However, I would argue that you can make decoupled models even without a direct dependency on Angular services. Creating self contained objects with only necessary dependencies and responsibilities is probably the way to go. It also makes life a lot easier when doing automated testing. Single responsibility is a buzz work these days, but it does make a lot of sense!
Here is an example of a patter I consider good for decoupling the object model from the dom.
http://www.syntaxsuccess.com/viewarticle/548ebac8ecdac75c8a09d58e
A key goal is to structure your code in a way that makes it just as easy to use from a unit tests as from a view. If you achieve that you are well positioned to write realistic and useful tests.
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):
angular.module('job.models', [])
.service('Job', ['Restangular', function(Restangular) {
var Job = Restangular.service('jobs');
Restangular.extendModel('jobs', function(model) {
model.getResult = function() {
if (this.status == 'complete') {
if (this.passed === null) return "Finished";
else if (this.passed === true) return "Pass";
else if (this.passed === false) return "Fail";
}
else return "Running";
};
return model;
});
return Job;
}]);
$resource (slightly more convoluted):
angular.module('job.models', [])
.factory('Job', ['$resource', function($resource) {
var Job = $resource('/api/jobs/:jobId', { full: 'true', jobId: '#id' }, {
query: {
method: 'GET',
isArray: false,
transformResponse: function(data, header) {
var wrapped = angular.fromJson(data);
angular.forEach(wrapped.items, function(item, idx) {
wrapped.items[idx] = new Job(item);
});
return wrapped;
}
}
});
Job.prototype.getResult = function() {
if (this.status == 'complete') {
if (this.passed === null) return "Finished";
else if (this.passed === true) return "Pass";
else if (this.passed === false) return "Fail";
}
else return "Running";
};
return Job;
}]);
$http (hardcore):
angular.module('job.models', [])
.service('JobManager', ['$http', 'Job', function($http, Job) {
return {
getAll: function(limit) {
var params = {"limit": limit, "full": 'true'};
return $http.get('/api/jobs', {params: params})
.then(function(response) {
var data = response.data;
var jobs = [];
for (var i = 0; i < data.objects.length; i ++) {
jobs.push(new Job(data.objects[i]));
}
return jobs;
});
}
};
}])
.factory('Job', function() {
function Job(data) {
for (attr in data) {
if (data.hasOwnProperty(attr))
this[attr] = data[attr];
}
}
Job.prototype.getResult = function() {
if (this.status == 'complete') {
if (this.passed === null) return "Finished";
else if (this.passed === true) return "Pass";
else if (this.passed === false) return "Fail";
}
else return "Running";
};
return Job;
});
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.
I have a DB with a collection of games and a collection of comments about those games. each game has a unique ID and each comment has a field game_id.
in the client side, I wrote an async function in the scope that retrieves the number of comments for a game from the db and return a promise. what is the right (and most cleanest) way to show the value in the HTML ?
I know there are different ways to do this. I am looking for the simplest and cleanest way.
Will this code work?
//code in HTML
<div ng-repeat="game in games">
<span>{{game.date}}</span>
<span>{{game.home}}</span>
<span>{{game.away}}</span>
<span>{{getNumberOfComments(game.id)}} Comments</span>
</div>
app.factory('apiService', function($http) {
return {
getGames: function() {
return $http.get('/api/games',{cache: true});
},
getCommentsCount: function(game_id) {
return $http.get('/api/CommentsCount/' + id);
}
};
});
app.controller('MyCtrl', function ($scope, apiService) {
gameService.getGames().then(function(resp) {
$scope.games = resp.data;
});
$scope.getNumberOfComments = function(id){
return apiService.getCommentsCount(id).then(function(resp){
return resp.data;
});
});
});
Followup question:
if I have many games (lets say 30), is it a bad idea to send 30 HTTP requests to the server for each game? is it better to send one request called "getAllComments" and retrieve the comments count for each game from the repsonse ?
for example:
function getAllComments() {
$http.get('/allComments',function(data){
$scope.games.forEach(function(game){
game.commentsCount = data[game.id];
});
});
}
//code in HTML
<div ng-repeat="game in games">
<span>{{game.commentsCount}} Comments</span>
</div>
This seems like a good solution - but the problem starts when i have more than 30 games but i want to show 30 games to the user and give him the possibility to load more. Then im actually asking more comments than I need and i have to run this function every time the user loads more games. it seems like a complicated and efficient solution.
EDIT:
One solution is to aggregate the information on the server side and have the client send 1 HTTP request and the response will include all the information. To implement this I will need to iterate the list of games in the server side and for each game retrieve the comments count from the comment collection. The downside of this solution is that the it will take longer for the client to see the list of games. I rather the client to first see the list of games as soon as possible and then after that to load the number of comments. Another downside is the in NodeJS (the backend framework that I'm using), a code like that will block the main thread from serving other clients. Therefore I do not want to make any iterations on the server side.
One more thing that needs to take into account is caching. I want to save the HTTP request that retrieves the games in the $http cache. Because the list of games is static and not going to change, unlike the comments count, that is always increasing. This way when the client moves to a different route and comes back to the games list, the games will show up immediately. But I do want to load the comments count every time the user comes back to the games page.
A clean way to do this would be to first move the code that makes the http call to a service:
app.factory('gameService', function($http) {
return {
getGames: function() {
return $http.get('/api/games');
}
};
});
Then, inject the service in your controller, call the get method and set the value on scope to be used by the view:
app.controller('MyCtrl', function ($scope, gameService) {
$scope.model = {};
gameService.getGames().then(function(resp) {
$scope.model.games = resp.data;
});
});
And your html would be like this:
<div ng-repeat="game in model.games">
<span>{{game.comments.length}} Comments</span>
</div>
Note that this assumes that the endpoint returns all the games and each game has a field for the comments. For example, the schema for a Game might look like this:
{
"id": "1",
"name": "The best game",
"comments": [
{
"id": "c1",
"content": "The is the best game ever"
}
]
}
I'm using Angular to develop commenting functionality for a web app.
Currently there are two sections in the application were a user can comment:
Category
Product
About 90% of the commenting functionality is the same for both sections and as such I would like to make this reusable - i.e write some service or controller that I can reference/use as a base.
So far, my research seems to point to using a factory service but unfortunately this doesn't seem to work (I've spent the whole day running through various tutorials).
It is quite possible that I am over thinking this and making it far too complicated but I honestly don't know which way to turn anymore.
Herewith a quick and dirty overview of what I have so far:
HTML view for the category
Controller for the category (receives data from service and posts data to service in order to bind data to model)
Service for the category (retrieve and stores all the necessary
data)
The product uses the same logic and a lot of the code in the service and controller will be duplicated.
I've merged the two services into one service successfully but I'm having trouble doing the same for the controller.
Do I:
Write a base controller that will communicate with the above mentioned service and that will hookup with the two existing controllers
OR
Write a factory/provider service that hooks up to the two existing controllers as well as the above mentioned service.
If you go the route of using a factory, you could put all the common functionality into its return object and reference that from your controllers.
Factory
angular.module('myModule').factory('CommonFunctions', function(){
return {
foo : foo,
bar : bar
}
function foo(){
console.log('foo');
};
function bar (){
console.log('bar');
};
}
Controller
angular.module('myModule')
.controller('myController', ['CommonFunctions', function(CommonFunctions) {
var vm = this;
vm.foo = CommonFunctions.foo();
vm.bar = CommonFunctions.bar();
}
angular's separation of service types ie:
for specific values
constant
value
(constant for specific values needed before other services are created)
for functions
factory
service
provider
(provider for specific instances when you need a services before other services are created, usually taking advantage of constants)
allow the ability to share data and ways to process that data between controllers and or directives, anything that can be a value can also be a constant, the only difference there being where they can be injected. Similarly any service can be rewritten to a factory or a provider, it is more your specific use case / what your more comfortable writing that would determine which to use, but really the best way to think about it is if you have a value that needs to be shared but is not needed inside angular.module.config then use value, otherwise use constant, now if you have a single function that you want to share, (maybe it processes that value in some way or maybe it just does something else) you should write it as a factory, then when you have a few of those factory's that deal with either that value, or anything else, you can combine them into a service or configure and combine them using a provider. here is a simple example (note i am using the recommended syntax for writing angular services):
'use strict';
var app = angular.module('test.app',[]);
app.constant('configureableValue',{defaultValue:55});
app.value('editableValue',{defaultValue:100,editedValue:null});
app.provider('configureValue',configureValueProvider);
configureValueProvider.$inject - ['configureableValue'];
function configureValueProvider(configureableValue){
var defaultVal = configureableValue.defaultValue,
originalVal = defaultVal;
return {
getValue:getValue,
setValue:setValue,
resetValue:resetValue,
'$get':providerFunc
};
function getValue(){
return defaultVal;
}
function setValue(val){
defaultVal = val;
}
function providerFunc(){
return {
get:function(){ return getValue(); },
reset:function(){ resetValue(); }
};
}
function resetValue(){
defaultVal = originalVal
}
}
// this factory is an example of a single function service, this should almost always be defined as a factory
app.factory('getEditableValue',getEditableValue);
getEditableValue.$inject = ['editableValue'];
function getEditableValue(editableValue){
return function(){
return editableValue.editedValue ? editableValue.editedValue : editableValue.defaultValue;
};
}
// same with this one
app.factory('setEditableValue',setEditableValue);
setEditableValue.$inject = ['editableValue'];
function setEditableValue(editableValue){
return function(val){
editableValue.editedValue = val;
}
}
// now this is an example of a service service collecting the factorys for an object with all the related behavior we need
app.service('editableService',editableService);
editableService.$inject = ['getEditableValue','setEditableValue'];
function editableService(getEditableValue,setEditableValue){
var self = this;
self.setVal = setEditableValue;
self.getVal = getEditableValue;
}
app.config(appConfig);
appConfig.$inject = ['configureValueProvider'];
function appConfig(configureValueProvider){
configureValueProvider.setValue('i changed '+ configureValueProvider.getValue() +' to this!!!!');
}
app.run(appRun);
appRun.$inject = ['configureValue','editableService'];
function appRun(configureValue,editableService){
console.log('before editing: ',editableService.getVal());
editableService.setVal('changed!!!');
console.log('after editing: ',editableService.getVal());
console.log('we changed this in the config func: ',configureValue.get());
configureValue.reset();
console.log('and now its back to the original value: ',configureValue.get());
}
i know thats a lot for a simple example, but there are a lot of features provided by angular, and many ways to use them, hopefully this helps.
The App I'm building is complex and the data is huge. It is an order administration dispatch app.
At the beginning the first thing I have to do it to get from the API all the orders, all the stores and all the couriers.
During the life cycle of an order I receive several updates from the API and I handle everything through Server Sent Events.
Once I load the data I put everything in 3 different $scope: $scope.orders, $scope.stores and $scope.couriers and a I created a service to handle and share updated data between controllers.
One of the service look like this:
App.factory('Orders', ['logs', function (logs) {
var orders = {};
var newOrder = null;
var updatedOrder = null;
orders.list = [];
orders.getNewOrder = function () {
return newOrder;
};
orders.setNewOrder = function (order) {
newOrder = order;
};
orders.updateOneOrder = function (index, order) {
orders.list[index] = order;
updatedOrder = order;
};
orders.getUpdatedOrder = function () {
return updatedOrder;
};
orders.getAllOrders = function () {
return orders.list;
};
return orders;
}]);
And I $watch for those methods to see changes and update the relative $scope.
As you can see in my service I have one more Array to store data in order to be able to handle it due to the fact I can not use $scope in a Factory.
I followed a guide online regarding this. I know there is also Broadcast and Emit but probably it was too complex to handle all those things with that method.
One more thing I avoided is to store orders, stores and couriers in $rootScope.
I think the App is a bit slow and use a lot of memory to handle everything so I wish to know the best practice to handle huge data that need to be shared between controllers and updated in real time...
I think my method at the moment is using too much memory.
I'm new to angular unit test cases and I'm trying to write a unit test case for the following code that I have in my Angular controller. Data contains balance which is added up and assigned to $scope.Total
.success(function (data) {
$scope.Total = _.reduce(data, function(memo, value) {
return memo + value.Balance;
}, 0);
});
My test case looks as follows
it('should return Total', function () {
_http.whenGET('').respond([{
data: [
{ Balance: 5 },{Balance:10}]
}]);
_controller();
_http.flush();
expect(_scope.Total).toBe(15);
});
I keep getting the error Expected NaN to be 15.Error: Expected NaN to be 15.
Please let me know what I'm doing wrong here. Would be great if you can show me the correct code.
Thanks in advance.
I believe it is because of the incorrect data structure for the data stub in the httpBackend stubbing.
_http.whenGET('').respond([{
data: [
{ Balance: 5 },{Balance:10}]
}]);
Should be:
_http.whenGET('').respond([{ Balance: 5 },{Balance:10}]);
Looking at your implementation it looks like you are looking for array of objects with each object having balance property. Instead you are adding one more layer in the data structure.
Plnkr
A side note: You may want to abstract http calls to angular service and inject angular service in the controllers instead of directly using http. With that you achieve separation of concerns and single responsibility in your controller. Also you can easily create mocks for your service as well.