Hitesh Kumar Hitesh Kumar - 5 months ago 28
AngularJS Question

Why i am able to change angular constant?

I am playing with angular constants. I am observing that I am able to change the value of the

constant
. I am not able to get it. Why I am able to change the value. I am creating the constant like this:

var app = angular.module('app', []);
app.constant('Type', {
PNG: 'png',
GIF: 'gif'
});
app.constant('serialId', 'aRandomId');


Even if I create the constant using
angular.value
then also I am able to change it. To change the value of constant I am doing this in my controller:

app.controller('MainController', ['$scope', 'Type', 'serialId', '$timeout', function($scope, Type, serialId, $timeout) {
$scope.data = {
type: Type,
serialId: serialId
};
$timeout(function() {
Type.PNG = 'this is changed';
serialId = 'new Serial Id';
console.log(serialId);
}, 1000);
}]);


By the defination of constant what I get is constant is somehting whose value does not change and it has a fixed value. MDN says that once you declare the constant you can not change it if constant is not an object. eg.

const x=10;
x=20;//will throw the error.
const obj={};
obj.a='ab'; //will not throw error.


But in case of angular constant when I change the value nothing happens. It does not even notify that the value is changed. And there documentation also does not talk about changing the value of constants. If we can change the value of angular constant like a plain javascript variable then why do they are called constants? Here's the fiddle to play

Answer

There's a difference between:

  • Value types (strings, booleans, etc); and
  • Reference types (references to objects, arrays, etc);

A variable can be of either type.

Constant variables are called "constants" because you cannot change their content: you cannot set a new value or reference, respectively.

Put differently, for reference types being constant means you cannot change that variable so that it'll reference something else. As you noted, you can change the contents of whatever object the variable points to.

If you want to make an object itself "constant", you can use Object.freeze:

var app = angular.module('app', [])
  .constant('Type', Object.freeze({ PNG: 'png', GIF: 'gif' }))
  .constant('SerialId', 'asdfgh12345')
  .controller('myController', ['$timeout', 'Type', 'SerialId', MyController]);
  
function MyController($timeout, Type, SerialId) {
  var vm = this;
  
  // This .data property nor its properties are constant or frozen...
  vm.data = {
    type: Type,
    serialId: SerialId
  };
  
  $timeout(function() {
    Type.PNG = 'this is changed in timeout';
    SerialId = 'changed serial id in timeout';
  }, 1000);
  
  $timeout(function() {
    var el = document.getElementById('x');
    var injector = angular.element(el).injector();
    vm.secondData = {
      type: injector.get('Type'),
      serialId: injector.get('SerialId')
    }
  }, 2000);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
<div ng-app="app" ng-controller="myController as vm" id="x">
  <pre>{{ vm | json }}</pre>
</div>

But note that Object.freeze does not do so recursively, you'd need a function/library for that.

Note that I also snuck in some things comment about SerialId. First up, realize that there's three different things named "SerialId":

  1. An angular constant names "SerialId";
  2. A function argument named "SerialId";
  3. The third item (string) in the dependencies array, "SerialId";

When the controller constructor function is run, the function argument will be filled with the value from the constant. The function argument could've been named Foo as well, by the way. You should consider that argument to be a local non-constant variable with the same value as the constant has.