1

I'm trying to use inheritance in angular services, as explained here: http://blog.mgechev.com/2013/12/18/inheritance-services-controllers-in-angularjs/, I want to use the "Inject the parent" method.

However, it doesn't seem to work, and I can't see why.

var myApp = angular.module('myApp',[]);

angular.module('myApp').controller('MyCtrl', MyCtrl);
angular.module('myApp').factory('BaseModel', BaseModel);
angular.module('myApp').factory('ThreadModel', ThreadModel);
angular.module('myApp').factory('PostModel', PostModel);

function MyCtrl($scope, ThreadModel, PostModel) {
  $scope.tableNameForThreads = ThreadModel.getTableName();
  $scope.tableNameForPosts = PostModel.getTableName();
}

function BaseModel() {
  var tableName = "";

  var service = {
    init: init,
    getTableName: getTableName
  };

  return service;

  function getTableName() {
    return tableName;
  }

  function init(theTableName) {
    tableName = theTableName;
  }
}

function ThreadModel(BaseModel) {
  var service = Object.create(BaseModel);
  service.init("threads");
  return service;
}

function PostModel(BaseModel) {
  var service = Object.create(BaseModel);
  service.init("posts");
  return service;
}

The result is that ThreadModel.getTableName() returns "posts" in stead of "threads".

I tried both Object.create(...) and angular.copy(BaseModel, this), but both don't seem to make a deep copy.

JSFIDDLE: http://jsfiddle.net/dirkpostma/Lvc0u55v/3989/

What am I doing wrong here?

Dirk
  • 2,167
  • 2
  • 20
  • 29

2 Answers2

2

The problem is that with this set up using Object.create you produce services with the tableName variable stored in the same common closure (BaseModel function). To put it simply, init method modifies the same local tableName variable.

You could fix it like this:

function BaseModel() {

  var service = {
    init: init,
    getTableName: getTableName
  };

  return service;

  function getTableName() {
    return this._tableName;
  }

  function init(theTableName) {
    this._tableName = theTableName;
  }
}

Note, that getTableName and init methods now work with instance property this._tableName which is not shared between TableModel and PostModel instances.

Demo: http://jsfiddle.net/Lvc0u55v/3991/

dfsq
  • 191,768
  • 25
  • 236
  • 258
0

@dfsq has already well explained and given a simple solution. I put here what I am thinking about this issue.

In your code Object.create(BaseModel) creates a new object whose prototype is a returned value of BaseModel function. In those children models init method modifies tableName within the local scope of BaseModel function. If you replace tableName with this.tableName, that will work as you expected: both init and getTableName methods will actually modify/call tableName property of service variable within ThreadModel or PostModel functions. But it looks complicated.

In your case I would like suggest the following service inheritance solution, which would be clearer. There is an other post that can be interesting.

var myApp = angular.module('myApp', []);

angular.module('myApp').controller('MyCtrl', MyCtrl);
angular.module('myApp').service('BaseModel', BaseModel);
angular.module('myApp').service('ThreadModel', ['BaseModel', ThreadModel]);
angular.module('myApp').service('PostModel', ['BaseModel', PostModel]);

function MyCtrl($scope, ThreadModel, PostModel) {
  $scope.tableNameForThreads = ThreadModel.getTableName();
  $scope.tableNameForPosts = PostModel.getTableName();
}

function BaseModel() {
  this.tableName = "";

  this.getTableName = function() {
    return this.tableName;
  }

  this.init = function(theTableName) {
    this.tableName = theTableName;
  }
}

function ThreadModel(BaseModel) {
  angular.extend(ThreadModel.prototype, BaseModel);
  this.tableName = "threads";
}

function PostModel(BaseModel) {
  angular.extend(PostModel.prototype, BaseModel);
  this.tableName = "posts";
}

JSFiddle: http://jsfiddle.net/Lvc0u55v/3993/

Community
  • 1
  • 1
micmia
  • 1,371
  • 1
  • 14
  • 29