Let's say that I have an AngularJS data service that makes a call to the server and returns an object that can be extended with additional methods. For example, assume the following function is part of an AngularJS service for something like NerdDinner.
function getDinner(dinnerId) {
return $http.get('api/dinner/' + dinnerId)
.then(loadDinnerComplete)
.catch(loadDinnerFailed);
function loadDinnerComplete(response) {
return new Dinner(response.data);
}
}
What's the best practice for a place to define the Dinner class? Is that a factory in a separate file? Do I define that in the NerdDinner service? Or do I define that in the GetDinner class (assuming that's the only method that can create dinners)?
I did not find any specific reference to creating objects in the style guide, so forgive me if it's covered and I just missed it.
Edit I ultimately decided to accept Jeroen's answer because it most matched my needs for a rather simple use case. However, Daniel's answer is pure gold, and should not be overlooked. If I chose to extend the functionality of my DTO with simple CRUD or additional server based operations, a $resource is a great approach.
AngularJS starts automatically when the web page has loaded. The ng-app directive tells AngularJS that the <div> element is the "owner" of an AngularJS application. The ng-model directive binds the value of the input field to the application variable name.
When AngularJS starts your application, it parses and processes this new markup from the template using the compiler. The loaded, transformed and rendered DOM is then called the view. The first kind of new markup are the directives. They apply special behavior to attributes or elements in the HTML.
AnglarJS is an open source web application development framework developed by Google. You need to install Node. js on the computer system. You just need to add AngularJS file just like any other JavaScript file to use it in applications.
Where to place business entities (like Dinner) is not explicitly mentioned in John Papa's style guide (afaik).
If you want to go this route (using business entities and placing logic there), I should give each entity its own factory:
(function() {
'use strict';
angular
.module('myDinner')
.factory('Dinner', DinnerFactory);
function DinnerFactory() {
Dinner.prototype.eat = eat;
Dinner.prototype.cancel = cancel;
return Dinner;
// Constructor
function Dinner (data) {
// this is just an example:
this.time = data.time;
this.location = data.location;
}
// Methods
function eat() {
// ...
}
function cancel() {
// ...
}
}
})();
Then you can inject them into your controller or maybe other service objects, just using Dinner
, and create a new Dinner
object using new Dinner(data)
.
You could also use a service, but John Papa discourages services, because they are too similar to factories.
IMO what you are doing has already been done in $resource
and restacular. You are talking about "defining data objects" (or, models in typical parlance). That said, defining each "data object" as its own factory is the way to do it, as per John Papa's advice regarding single concerns.
John Papa talks about this in the factories section.
If you want to work this out by hand, IMO you could define your models and append methods on each which would represent various crud operations. This is the pattern $resource
and restangular (sort of) take.
//Dinner model as angular factory, each of these methods returns a promise
function Dinner($http) {
return {
create: function(route, body) { /** http.post */ },
get: function(route) { /** http.get */ },
update: function(route, body) { /** http.put */ },
destroy: function(route) { /** http.delete */ }
};
}
now your Dinner
model has convenient crud methods built in so you could do
var dinner = new Dinner;
dinner.get("/api/dinner/1").then() //get dinner with id of 1
dinner.update("/api/dinner/1", {name: "burger"}).then() //update dinner with id of 1
Edit:
So if you want to create an object that isn't concerned with retrieving data, IMO you should create another factory that requires your model. This decouples your data retrieval from your data manipulation. In OPs original example, the data retrieval and manipulation are tightly coupled.
function Meal(dinner) {
//this.meal is the specified dinner
this.meal = new Dinner().get("/api/dinner" + dinner);
//some random build-in data manipulation methods
return {
getCalories: function() { return this.meal * 400; },
getPrice: function() { return (this.meal * 100) + "$"; }
};
}
Now that you've separated your data-manipulation into a separate object you can do something like this (this is a synchronous example though)
var mcdonalds = new Meal(/** specify which dinner */)
mcdonalds.getPrice() //$4.56
mcdonalds.getCalores() //9999
Personally I have used both Services
and Factories
for creating objects. From John Papa's style guide however:
Services are instantiated with the
new
keyword, usethis
for public methods and variables. Since these are so similar to factories, use a factory instead for consistency.
Following the SRP, you should put it in a new Service
or Factory
not part of another.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With