3

I have tens of AngularJS Factories which have a lot in common. So I'm trying to make a base class and subclass it.

But I've noticed that the subclasses are sharing member variables of the base class.

I've made an example on http://jsbin.com/doxemoza/2/edit, the code is also posted here:

HTML:

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body ng-app="demo" ng-controller="MainCtrl">
  <p>ChildService: {{value1}}</p>
  <p>AnotherChildService : {{value2}}</p>
</body>
</html>

JavaScript:

angular.module('demo', []);

var demo = angular.module('demo').controller('MainCtrl', function ($scope, ChildService, AnotherChildService) {
  $scope.value1 = ChildService.getPrivateVar();
  $scope.value2 = AnotherChildService.getPrivateVar();
});

var Base = function () {

  var Service = {};

  Service.privateVar = 0;

  Service.setPrivateVar = function (value) {
    Service.privateVar = value;
  }

  Service.getPrivateVar = function () {
    return Service.privateVar;
  }

  return Service;
};

demo.factory('BaseService', Base)


demo.factory('ChildService', function (BaseService) {
  var ChildService = Object.create(BaseService);
  ChildService.setPrivateVar(1);
  return ChildService;
});

demo.factory('AnotherChildService', function (BaseService) {
  var AnotherChildService = Object.create(BaseService);
  AnotherChildService.setPrivateVar(2);
  return AnotherChildService;
});

My expected output is:

ChildService: 1

AnotherChildService : 2

But instead I get:

ChildService: 2

AnotherChildService : 2

I think ChildService and AnotherChildService are sharing the same privateVar, so I got the same value for them.

How should I change the code to make them use different instance of privateVar?

Thanks

Zhao Xiang
  • 1,625
  • 2
  • 23
  • 40

2 Answers2

1

I use something like this:

angular.module('app')
.controller('ParentController', ['$scope', 'dataService', function ($scope, dataService) {
   //controller logic here
}])
.controller('ChildController', ['$scope', '$controller', 'SomeDataService', function ($scope, $controller, SomeDataService) {
//extend your parentcontroller
    $scope.init = function () {
        //do something
    }
    angular.extend(this, $controller('ParentController', {
        $scope: $scope,
        dataService: SomeDataService
    }));
}])
.factory('BaseDataService', ['logger', function (logger) {
  var privateArray = [];

  return {
   publicFunction: function(){ return privateArray; },
   publicVar: "some var"
  }
}])
.factory('SomeDataService', ['BaseDataService', function (dataService) {
    var service = angular.extend({}, dataService);
    service.privateFunction = function () {
        //some code
    }
    service.privateVar= "some value";
    return service;
}]);

I combined all of them in this example.

Hope this helps.

Edit: this uses the mixin pattern described in this post

Community
  • 1
  • 1
Raphael Müller
  • 2,180
  • 2
  • 15
  • 20
  • So the key is to use `angular.extend`? I've tried that but it does not work eithor, see: http://jsbin.com/qosibope/1/edit – Zhao Xiang Jul 09 '14 at 06:11
  • I've also tried `angular.copy` which also does not work – Zhao Xiang Jul 09 '14 at 06:11
  • I tried something. http://jsbin.com/qosibope/3/edit And it works, when you access the variable directly. – Raphael Müller Jul 09 '14 at 06:19
  • I think use of functions is really necessary for my project, or I'll have to do a lot of copy/paste and lost the benefit of object-orented programming – Zhao Xiang Jul 09 '14 at 06:38
  • I have to agree, the behaviour is a little bit strange... The defined functions are called within the base context. And the variables in the child context. That's not really usefull... I use this abstraction mechanism on a lot of services, but never used a function which was a setter for a private variable. Most of the time I override the function and/or the variable. – Raphael Müller Jul 09 '14 at 06:45
  • I've checked that if I use `angular.copy`, `angular.extend` or `Object.create`, `ChildService.getPrivateVar === BaseService.getPrivateVar` is always true. Maybe it's an dead end – Zhao Xiang Jul 09 '14 at 07:06
1

I had that same problem, and was solved when a declared my BaseService this way:

demo = angular.module('demo', []);

demo.factory('BaseService', function(){
  return {
    privateVar: 0,

    setPrivateVar: function (value) {
      this.privateVar = value;
    },

    getPrivateVar: function () {
        return this.privateVar;
    }
  }
});

My "child" services are like yours. Everything works very fine.

Lucas
  • 124
  • 3
  • This indeed works. But can you or anyone else explain why this works? It seems to do be doing the same thing, but obviously internally working a little differently. – robinmitra Jan 01 '15 at 19:39
  • I've also noted that setting ```privateVar``` directly like ```ChildService.privateVar = 5``` also works fine, as long as you also access like like ```ChildService.privateVar```. That is, your code works fine if not using getter and setter. – robinmitra Jan 01 '15 at 23:23