2

i'm just starting to pick up ionic and angular and having trouble with understanding the implications of using factories/services for modeling.

Scenario:

  • I want to model a Contact object that has attributes and methods.
  • I want to an object AddressBook that has an attribute comprising a list of Contact objects and also has methods on it.

from all the literature i've read, I want to create a factory/service for Contact and one for AddressBook.

But since Contact is a singleton, how do I put together an array of these things? once i "create" a new contact, because it is a singleton, doesn't it just make changes on the original?

angular.module('blah.services', ['ionic.utils'])
.factory('Contact', function($q){
    var o = {
            "name" : '',
            "email" : ''
    };
    return o;
})
.factory('AddressBook', function($q, Contact){

   var o = {};
   o.contacts = [];

   o.addContact = function(contact){
      o.contacts.push(contact);
   }

   return o;
});

// and then somehwere something like
angular.module('blah.controllers', ['blah.services'])
.controller('somethingCtrl', function($scope, Contact, AddressBook) {

  /* what am i supposed to do here?
  is it something like
  var c = new Contact();
  c.name = 'foo';
  AddressBook.addContact(c);
  */

});

here's some of what i read already but still can't figure this out.

http://viralpatel.net/blogs/angularjs-service-factory-tutorial/
http://mcgivery.com/ionic-using-factories-and-web-services-for-dynamic-data/
https://docs.angularjs.org/guide/providers
angular.service vs angular.factory

Community
  • 1
  • 1
w--
  • 6,427
  • 12
  • 54
  • 92
  • 1
    Look at the first code snippet here, it explains how to get your factory working: http://stackoverflow.com/a/20809629/624590 . More or less, return a function in your factory; `new` acts on functions, calls the a new instance of the function as a constructor (i.e. where `this` is the new instance), and typically returns a copy of it (as long as you don't return another Object). I'd suggest writing/using a `.create` function rather than `new` though. – DRobinson May 16 '15 at 20:05
  • This is a great comment. good enough to be an answer. Thank you. – w-- May 21 '15 at 06:57

3 Answers3

4

You are, for some reason, thinking of Angular services as something different than what they actually were meant to be. In Angular case, they are just used as elements of Dependency Injection pattern that Angular embraces. They have no say with regards to Object-Orientedness.

What I mean by that is if you have a "class" function Contact (not the the service you created):

function Contact(name){
  this.name = name;
}

then you could do var contact1 = new Contact("foo"); and it doesn't necessitate there being an Angular service.

You probably should still create an Angular injectable, if you want to inject them and mock-test later, but it's nothing more than a function value, and so you could just use .value:

.value("Contact", function Contact(){...})
.controller("SomeCtrl", function($scope, Contact){
   $scope.contact1 = new Contact("foo");
}
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • thank you for showing me this. I would not have thought to use value in this manner. very helpful. – w-- May 21 '15 at 06:58
1

/* what am i supposed to do here? is it something like var c = new Contact();

Hmmm no, it's not. The Contact you pass to the controller refers to whatever your factory returns, which is the object o you created.

Another approach you may want to explore is to create a "model" factory and return an object with a "Contact" function (that you can instantiate in the controller) and an "addressBook" object that contains a "contacts" array. For example:

angular.module('blah.services', ['ionic.utils'])

.factory('model', function($q){
    return {
        Contact : function(name, email) {
            this.name = name;
            this.email = email;
        },
        addressBook : {
            contacts: []
        }
    };
})

.controller('somethingCtrl', function($scope, model) {
    // Add a contact to the address book
    model.addressBook.push(new model.Contact("foo", "foo@bar"));
    // Now model.addressBook contains a new model.Contact
});

You may also create the contacts array as a local/private variable in the factory (as in var contacts = []; outside the return statement) and add custom getters/setters in the addressBook object to manipulate it.

chris
  • 1,787
  • 1
  • 13
  • 13
0

I applaud you for your OOP thinking here. It is definitely a good way to go about things. Unfortunately, you are thinking in Classes and ES5 does not currently support this. My advice for you is to not let go of this approach that you have and instead start writing ES6 code and transpile it into ES5. This way you could make Contacts be a Class and AddressBook will indeed contain an array of Contacts.

As far as more resource on ES6 go, you should check some transpiling shims and plugins from the links below:

Dan Moldovan
  • 3,576
  • 2
  • 13
  • 24
  • Javascript does not need Classes. ES6 classes are syntactic sugar for functionality that already exists. `new` will work just fine in ES5 for creating new instances. (Further, but this is an opinion: `class`, and `new` are actually bad practice in Javascript, and simply being added to placate developers who are coming from other language and don't want to learn new ways) – DRobinson May 16 '15 at 20:04
  • (I hope that didn't come off anti-OO; I think Object Oriented can be very useful. I write plenty of my Javascript in an OO fashion. I just believe ES6 classes aren't the best way to go about it. More importantly to this question, however, they aren't the *necessay* way to go about it - if that were the case then inherently a shim wouldn't be possible.) – DRobinson May 16 '15 at 20:16