Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using John Papa's AngularJS style guide, what is the proper way to declare data objects?

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.

like image 918
villecoder Avatar asked Jan 07 '16 20:01

villecoder


People also ask

How does AngularJS application start?

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.

How does AngularJS application work?

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.

Can I use node js with AngularJS?

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.


3 Answers

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.

like image 140
Jeroen Noten Avatar answered Oct 31 '22 13:10

Jeroen Noten


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
like image 4
Daniel Lizik Avatar answered Oct 31 '22 12:10

Daniel Lizik


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, use this 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.

like image 2
Ryan Sayles Avatar answered Oct 31 '22 12:10

Ryan Sayles