0

I have the following angularJS service

define(["angular"], function(Angular) {

var dataStorageService = function() {
    var serviceConstructor = function() {
        var _getColor = function(color) {
            return this.config.categoryColorMapping.colors[color];
        }
    }

    var serviceInstance = new serviceConstructor();

    angular.extend(serviceInstance.prototype, {
        config: {
            numberOfMessagesDisplayed: 5,
            maxTitleLength: 48,
            maxPreambleLength: 140,
            categoryColorMapping: {
                colors : {
                    nyheter: '#2B2B2B',
                    sport: '#F59331',
                    underholding: '#F9B00D'
                },
                categories: {
                    nyheter: _getColor('nyheter'),
                    sport: _getColor('sport'),
                    underholding: _getColor('underholding')
                }
            }
        },
        get: function(param) {
            if(this.config.hasOwnProperty(param)) {
                return this.config[param];
            } else {
                console.warn('Playlist::configService:no "' + param + '" config found');
                return false;
            }
        },
        set: function(param, value) {
            this.config[param] = value;
        }
    });

    return serviceInstance;
};

return dataStorageService;
});

now my goal is to make public the following methods:

  1. get
  2. set

and I want '_getColor' method private but I want to use it within the JSON object config. When I run the code I have

"ReferenceError: _getColor is not defined"

is it possibie to achievie it this way? (to have _getColor private and use it within the JSON object within angular.extend?)

Oskar Szura
  • 2,469
  • 5
  • 32
  • 42

3 Answers3

1

Functions added to the prototype are defined outside the lexical scope of the constructor, and therefore have no access to "private" methods.

The former are shared between all instances, and the latter are per-instance. The only way to get around this is to explicitly export the (per-instance) function as a property of the instance, making it non-private.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Private methods can be added when you set the prototype to the return value of on IIFE. A method is not instance specific so it's possible. Instance specific members need to be defined in the constructor function and so do all privileged methods that need to access them. There are other pattern to simulate protected such as this one: http://stackoverflow.com/a/21800194/1641941 – HMR Oct 23 '14 at 14:12
1

Functions can be shared and still be private, instance specific private members have to be defined in the constructor though. Since your private function doesn't need to access instance specific private members you can do the following:

define(["angular"], function(Angular) {

var dataStorageService = function() {
    var serviceConstructor = function() {
    }

    var serviceInstance = new serviceConstructor();
    //IIFE returning object that will have private members as closure
    // privileged methods have to be in the same function body as the
    // private fucnction
    serviceInstance.prototype = (function() {
        var _getColor = function(instance, color) {
            return instance.config.categoryColorMapping.colors[color];
        };
        return {
          constructor: serviceConstructor
          ,config: {
            numberOfMessagesDisplayed: 5,
            maxTitleLength: 48,
            maxPreambleLength: 140,
            categoryColorMapping: {
                colors : {
                    nyheter: '#2B2B2B',
                    sport: '#F59331',
                    underholding: '#F9B00D'
                },
                categories: {
                    //since categories is a sub object of serviceinstance.categorycolormapper
                    // it is not possible to get the instance of serviceinstance
                    // at this time unless you set it in the constructor
                    // solution could be that each serviceinstance has it's own categorycolormaper
                    // and when categorycolormapper is created pass the serviceinstance instance
                    nyheter: _getColor(this,'nyheter'),
                    sport: _getColor(this, 'sport'),
                    underholding: _getColor(this, 'underholding')
                }
            }
          },
          get: function(param) {
            if(this.config.hasOwnProperty(param)) {
                return this.config[param];
            } else {
                console.warn('Playlist::configService:no "' + param + '" config found');
                return false;
            }
          },
          set: function(param, value) {
            this.config[param] = value;
          }
        }
    }());

    return serviceInstance;
};

return dataStorageService;
});

More info on constructor functions and prototype can be found here: https://stackoverflow.com/a/16063711/1641941

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
0

Within the definition of serviceConstructor add following line, after definition of _getColor

serviceConstructor.prototype._getColor = _getColor ;
Dinesh
  • 1,126
  • 9
  • 6
  • and every time you create a new instance then _every_ instance will be using a reference to _that particular_ instance's version of the function, and that function will only have access to _that instance's_ other private variables and methods. – Alnitak Oct 23 '14 at 09:38
  • Scope of _getColor is completly blocked within the constructor. use any reference variable declared in dataStorageService as channel to get availability of _getColor. //dataStorageService... var internal={}; //... within constructor internal._getColor = _getColor; – Dinesh Oct 23 '14 at 09:39